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

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

222 lines 9.1 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('Quote Preview', 'easy-invoice'); ?></h2>
15 <p class="mt-1 text-sm text-gray-500"><?php esc_html_e('Live preview of your quote', 'easy-invoice'); ?></p>
16 </div>
17 </div>
18 <?php if($quote_id > 0 ){ ?>
19 <a href="<?php echo esc_url(get_permalink($quote_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 = $quote ? $quote->getTemplate() : 'standard';
30 if (empty($current_template)) {
31 $current_template = 'standard';
32 }
33 $template_class = 'template-' . $current_template;
34
35 // Initialize formatter for the quote templates
36 $formatter = new \EasyInvoice\Helpers\QuoteFormatter($quote);
37 ?>
38
39 <div id="quote-container" class="quote-container quote-preview <?php echo esc_attr($template_class); ?>" style="position: relative;">
40 <?php
41 // Dynamically load the selected template from quote templates directory
42 // Dynamically load the selected template
43 $template_file = easy_invoice_design_template( 'quote', (string) $current_template );
44 if ( ! $quote instanceof \EasyInvoice\Models\Quote ) {
45 // Nothing to render yet; the designs read the model directly.
46 ?>
47 <div class="ei-preview-empty" style="text-align:center;padding:48px 24px;color:#64748b;">
48 <p style="margin:0;font-size:14px;"><?php esc_html_e( 'Your quote will appear here as you fill in the form.', 'easy-invoice' ); ?></p>
49 </div>
50 <?php
51 } elseif (file_exists($template_file)) {
52 include_once $template_file;
53 } else {
54 // Show template not found message
55 ?>
56 <div class="template-not-found">
57 <div class="template-not-found-icon">
58 <i class="fas fa-exclamation-circle"></i>
59 </div>
60 <h3><?php esc_html_e('Template Not Found', 'easy-invoice'); ?></h3>
61 <p>The selected template "<?php echo esc_html($current_template); ?>" does not exist.</p>
62 <p><?php esc_html_e('Please select a different template or contact support if you believe this is an error.', 'easy-invoice'); ?></p>
63 </div>
64 <style>
65 .template-not-found {
66 text-align: center;
67 padding: 40px;
68 background: #fff;
69 border-radius: 8px;
70 box-shadow: 0 2px 4px rgba(0,0,0,0.1);
71 }
72 .template-not-found-icon {
73 font-size: 48px;
74 color: #dc3545;
75 margin-bottom: 20px;
76 }
77 .template-not-found h3 {
78 font-size: 24px;
79 color: #32325d;
80 margin-bottom: 10px;
81 }
82 .template-not-found p {
83 color: #8898aa;
84 margin-bottom: 10px;
85 }
86 </style>
87 <?php
88 }
89 ?>
90 </div>
91 </div>
92
93 <style>
94 /* Loading state styles */
95 .loading-overlay {
96 position: absolute;
97 top: 0;
98 left: 0;
99 right: 0;
100 bottom: 0;
101 background: rgba(255, 255, 255, 0.9);
102 display: flex;
103 justify-content: center;
104 align-items: center;
105 z-index: 1000;
106 }
107
108 .loading-spinner {
109 text-align: center;
110 }
111
112 .quote-container.loading .loading-overlay {
113 display: flex;
114 }
115
116 /* (Resize logic removed per request) */
117 </style>
118
119 <script>
120 jQuery(document).ready(function($) {
121 // Localize easyInvoice object for this page
122 // Localize easyInvoice object for this page
123 window.easyInvoiceQ = window.easyInvoice || {};
124 window.easyInvoice.isPro = <?php echo easy_invoice_has_pro() ? 'true' : 'false'; ?>;
125 window.easyInvoice.ajaxUrl = '<?php echo esc_url(admin_url('admin-ajax.php')); ?>';
126 window.easyInvoice.nonce = '<?php echo esc_attr(wp_create_nonce('easy_invoice_nonce')); ?>';
127
128 // Function to update the live preview using AJAX
129 function updateLivePreview(templateId) {
130 $('#quote-container').addClass('loading');
131 $.ajax({
132 url: window.easyInvoice.ajaxUrl,
133 type: 'POST',
134 data: {
135 action: 'easy_invoice_load_quote_template',
136 template: templateId,
137 form_data: jQuery('#quote-form').length ? jQuery('#quote-form').serialize() : '',
138 quote_id: <?php echo (int) ($quote ? $quote->getId() : 0); ?>,
139 nonce: window.easyInvoice.nonce
140 },
141 success: function(response) {
142 if (response.success) {
143 $('#quote-container').html(response.data.html);
144 } else {
145 $('#quote-container').html('<div class="error">Failed to load template: ' + response.data.message + '</div>');
146 }
147 $('#quote-container').removeClass('loading');
148 },
149 error: function(xhr, status, error) {
150 $('#quote-container').html('<div class="error"><?php esc_html_e('Failed to load template. Please try again.', 'easy-invoice'); ?></div>');
151 $('#quote-container').removeClass('loading');
152 }
153 });
154 }
155
156 // Listen for template changes
157 $('.template-card').on('click', function(e) {
158 const templateId = $(this).data('template-id');
159
160 // Check if this is a premium template in free version
161 const isPremium = $(this).attr('data-is-premium') === 'true';
162 const isFreeVersion = !window.easyInvoice || !window.easyInvoice.isPro;
163
164 if (isPremium && isFreeVersion) {
165 // Show premium upgrade modal instead of selecting the template
166 if (typeof EasyInvoiceConfirmation !== 'undefined' && EasyInvoiceConfirmation.showFeatureUpgrade) {
167 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 quotes a unique and professional look.', 'easy-invoice')); ?>');
168 } else {
169 alert('<?php echo esc_js(__('This is a premium feature. Please upgrade to Pro to access all templates.', 'easy-invoice')); ?>');
170 }
171 return;
172 }
173
174 updateLivePreview(templateId);
175 });
176
177
178
179
180
181 // Live means live: re-render as the builder changes, not only when the
182 // template changes. Debounced so typing a description does not fire a
183 // request per keystroke.
184 (function () {
185 var $form = $('#quote-form'); if (!$form.length) { return; }
186 var timer = null;
187 var current = function () { return $form.find('input[name="quote_template"]').val() || $('.template-card.selected, .template-card.active').first().data('template-id') || 'standard'; };
188 var schedule = function () { clearTimeout(timer); timer = setTimeout(function () { updateLivePreview(current()); }, 700); };
189 $form.on('input change', 'input, textarea, select', schedule);
190 $(document).on('click', '.add-item-button, .remove-item, .client-option, #remove-selected-client, .template-card', schedule);
191 $(document).on('easy_invoice_items_changed easy_invoice_client_changed', schedule);
192 })();
193
194 });
195 </script>
196
197 <script>
198 // Scale the A4 design to the preview pane instead of letting it reflow: the
199 // preview then shows the sheet the client will see, only smaller.
200 (function () {
201 var box = document.getElementById('quote-container');
202 if (!box || !window.ResizeObserver) { return; }
203 var SHEET = 794;
204 function fit() {
205 var sheet = box.querySelector('.template, [class*="template-"]');
206 if (!sheet) { box.style.height = ''; return; }
207 var avail = box.clientWidth;
208 if (avail <= 0) { return; }
209 var k = Math.min(1, avail / SHEET);
210 sheet.style.width = SHEET + 'px';
211 sheet.style.maxWidth = 'none';
212 sheet.style.transformOrigin = 'top left';
213 sheet.style.transform = k < 1 ? 'scale(' + k + ')' : '';
214 box.style.overflow = 'hidden';
215 box.style.height = k < 1 ? Math.ceil(sheet.offsetHeight * k) + 'px' : '';
216 }
217 new ResizeObserver(fit).observe(box);
218 new MutationObserver(fit).observe(box, { childList: true, subtree: true });
219 fit();
220 })();
221 </script>
222