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
← All changes | templates/quotes/live-preview.php +80 -30 2.1.0 → 2.4.4 View file →
@@ -1,7 +1,10 @@
1 -<!-- Right Panel - Live Preview -->
2 -<div class="bg-white shadow rounded-lg" style="padding: 10px;">
3 -
1 +<?php
2 +if ( ! defined( 'ABSPATH' ) ) {
3 + exit;
4 +}
5 +?><div class="bg-white shadow rounded-lg" style="padding: 10px;">
6 +
4 7 <div class="px-6 py-5 border-b border-gray-200" style="margin-bottom: 10px;">
5 8 <div class="flex justify-between items-center">
6 9 <div class="flex items-center">
7 10 <div class="flex-shrink-0 bg-indigo-100 rounded-lg p-3">
@@ -7,14 +10,14 @@
7 10 <div class="flex-shrink-0 bg-indigo-100 rounded-lg p-3">
8 11 <i class="fas fa-eye text-indigo-600 text-xl"></i>
9 12 </div>
10 13 <div class="ml-4">
11 - <h2 class="text-lg font-medium text-gray-900">Quote Preview</h2>
12 - <p class="mt-1 text-sm text-gray-500">Live preview of your quote</p>
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>
13 16 </div>
14 17 </div>
15 18 <?php if($quote_id > 0 ){ ?>
16 - <a href="<?php echo 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">
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">
17 20 <i class="fas fa-external-link-alt mr-2"></i>
18 21 Full Preview
19 22 </a>
20 23 <?php } ?>
@@ -27,21 +30,28 @@
27 30 if (empty($current_template)) {
28 31 $current_template = 'standard';
29 32 }
30 33 $template_class = 'template-' . $current_template;
31 -
34 +
32 35 // Initialize formatter for the quote templates
33 36 $formatter = new \EasyInvoice\Helpers\QuoteFormatter($quote);
34 37 ?>
35 -
36 - <div id="quote-container" class="quote-container quote-preview <?php echo $template_class; ?>">
38 +
39 + <div id="quote-container" class="quote-container quote-preview <?php echo esc_attr($template_class); ?>" style="position: relative;">
37 40 <?php
38 41 // Dynamically load the selected template from quote templates directory
39 - $template_file = EASY_INVOICE_PLUGIN_DIR . 'templates/quote-templates/' . $current_template . '.php';
40 -
41 - if (file_exists($template_file)) {
42 - require $template_file;
43 - } else {
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 {
44 54 // Show template not found message
45 55 ?>
46 56 <div class="template-not-found">
47 57 <div class="template-not-found-icon">
@@ -46,11 +56,11 @@
46 56 <div class="template-not-found">
47 57 <div class="template-not-found-icon">
48 58 <i class="fas fa-exclamation-circle"></i>
49 59 </div>
50 - <h3>Template Not Found</h3>
60 + <h3><?php esc_html_e('Template Not Found', 'easy-invoice'); ?></h3>
51 61 <p>The selected template "<?php echo esc_html($current_template); ?>" does not exist.</p>
52 - <p>Please select a different template or contact support if you believe this is an error.</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>
53 63 </div>
54 64 <style>
55 65 .template-not-found {
56 66 text-align: center;
@@ -111,11 +121,11 @@
111 121 // Localize easyInvoice object for this page
112 122 // Localize easyInvoice object for this page
113 123 window.easyInvoiceQ = window.easyInvoice || {};
114 124 window.easyInvoice.isPro = <?php echo easy_invoice_has_pro() ? 'true' : 'false'; ?>;
115 -window.easyInvoice.ajaxUrl = '<?php echo admin_url('admin-ajax.php'); ?>';
116 -window.easyInvoice.nonce = '<?php echo wp_create_nonce('easy_invoice_nonce'); ?>';
117 -
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 +
118 128 // Function to update the live preview using AJAX
119 129 function updateLivePreview(templateId) {
120 130 $('#quote-container').addClass('loading');
121 131 $.ajax({
@@ -123,9 +133,10 @@
123 133 type: 'POST',
124 134 data: {
125 135 action: 'easy_invoice_load_quote_template',
126 136 template: templateId,
127 - quote_id: <?php echo $quote ? $quote->getId() : 0; ?>,
137 + form_data: jQuery('#quote-form').length ? jQuery('#quote-form').serialize() : '',
138 + quote_id: <?php echo (int) ($quote ? $quote->getId() : 0); ?>,
128 139 nonce: window.easyInvoice.nonce
129 140 },
130 141 success: function(response) {
131 142 if (response.success) {
@@ -135,37 +146,76 @@
135 146 }
136 147 $('#quote-container').removeClass('loading');
137 148 },
138 149 error: function(xhr, status, error) {
139 - $('#quote-container').html('<div class="error">Failed to load template. Please try again.</div>');
150 + $('#quote-container').html('<div class="error"><?php esc_html_e('Failed to load template. Please try again.', 'easy-invoice'); ?></div>');
140 151 $('#quote-container').removeClass('loading');
141 152 }
142 153 });
143 154 }
144 -
155 +
145 156 // Listen for template changes
146 157 $('.template-card').on('click', function(e) {
147 158 const templateId = $(this).data('template-id');
148 -
159 +
149 160 // Check if this is a premium template in free version
150 161 const isPremium = $(this).attr('data-is-premium') === 'true';
151 162 const isFreeVersion = !window.easyInvoice || !window.easyInvoice.isPro;
152 -
163 +
153 164 if (isPremium && isFreeVersion) {
154 165 // Show premium upgrade modal instead of selecting the template
155 166 if (typeof EasyInvoiceConfirmation !== 'undefined' && EasyInvoiceConfirmation.showFeatureUpgrade) {
156 - EasyInvoiceConfirmation.showFeatureUpgrade('<?php _e('Premium Templates', 'easy-invoice'); ?>', '<?php _e('Access all premium templates including Modern, Professional, Classic, and more to give your quotes a unique and professional look.', 'easy-invoice'); ?>');
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')); ?>');
157 168 } else {
158 - alert('<?php _e('This is a premium feature. Please upgrade to Pro to access all templates.', 'easy-invoice'); ?>');
169 + alert('<?php echo esc_js(__('This is a premium feature. Please upgrade to Pro to access all templates.', 'easy-invoice')); ?>');
159 170 }
160 171 return;
161 172 }
162 -
173 +
163 174 updateLivePreview(templateId);
164 175 });
165 -
166 176
167 -
168 177
169 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 +
170 194 });
171 -</script>
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>