| @@ -8,20 +8,13 @@ | ||
| 8 | 8 | |
| 9 | 9 | // Enqueue CSS and JS files for the builder page |
| 10 | 10 | wp_enqueue_style('easy-invoice-form', plugin_dir_url(__FILE__) . '../../assets/css/invoice-form.css', array(), EASY_INVOICE_VERSION); |
| 11 | 11 | wp_enqueue_script('easy-invoice-form', plugin_dir_url(__FILE__) . '../../assets/js/invoice-form.js', array('jquery'), EASY_INVOICE_VERSION, true); |
| 12 | -wp_enqueue_script('easy-quote-save', plugin_dir_url(__FILE__) . '../../assets/js/quote-save.js', array('jquery'), EASY_INVOICE_VERSION, true); | |
| 13 | 12 | wp_enqueue_script('easy-client-manager', plugin_dir_url(__FILE__) . '../../assets/js/client-manager.js', array('jquery'), EASY_INVOICE_VERSION, true); |
| 14 | 13 | |
| 15 | 14 | // Create nonce for AJAX calls |
| 16 | 15 | $ajax_nonce = wp_create_nonce('easy_invoice_nonce'); |
| 17 | 16 | |
| 18 | -// Localize script for AJAX URL and nonce | |
| 19 | -wp_localize_script('easy-quote-save', 'easyInvoice', array( | |
| 20 | - 'ajaxUrl' => admin_url('admin-ajax.php'), | |
| 21 | - 'nonce' => $ajax_nonce | |
| 22 | -)); | |
| 23 | - | |
| 24 | 17 | // Localize client manager script |
| 25 | 18 | wp_localize_script('easy-client-manager', 'easyInvoice', array( |
| 26 | 19 | 'ajaxUrl' => admin_url('admin-ajax.php'), |
| 27 | 20 | 'nonce' => $ajax_nonce |
| @@ -31,9 +24,9 @@ | ||
| 31 | 24 | $quote_id = isset($_GET['id']) ? intval($_GET['id']) : 0; |
| 32 | 25 | if ($quote_id) { |
| 33 | 26 | $post = get_post($quote_id); |
| 34 | 27 | if (!$post || $post->post_type !== \EasyInvoice\Constants\PostTypes::EASY_INVOICE_QUOTE_POST_TYPE) { |
| 35 | - wp_die(__('Invalid post type. This builder can only be used for quotes.', 'easy-invoice'), __('Invalid Post Type', 'easy-invoice'), array('back_link' => true)); | |
| 28 | + wp_die(esc_html__('That quote does not exist or has been deleted.', 'easy-invoice'), esc_html__('Quote not found', 'easy-invoice'), array('back_link' => true, 'response' => 404)); | |
| 36 | 29 | } |
| 37 | 30 | } |
| 38 | 31 | $quote = null; |
| 39 | 32 | |
| @@ -55,10 +48,10 @@ | ||
| 55 | 48 | $quote_declined_message = get_option('easy_invoice_quote_declined_message', __('Thank you for your consideration.', 'easy-invoice')); |
| 56 | 49 | |
| 57 | 50 | $quote_data = array( |
| 58 | 51 | 'number' => $quote_number_service ? $quote_number_service->getNextNumber() : 'QT-1', |
| 59 | - 'date' => date('Y-m-d'), | |
| 60 | - 'expiry_date' => date('Y-m-d', strtotime('+30 days')), | |
| 52 | + 'date' => current_time('Y-m-d'), | |
| 53 | + 'expiry_date' => wp_date('Y-m-d', strtotime('+30 days')), | |
| 61 | 54 | 'client_id' => 0, |
| 62 | 55 | 'client_name' => '', |
| 63 | 56 | 'client_email' => '', |
| 64 | 57 | 'client_phone' => '', |
| @@ -86,9 +79,11 @@ | ||
| 86 | 79 | ); |
| 87 | 80 | |
| 88 | 81 | // Get clients from repository |
| 89 | 82 | $client_repository = ClientServiceProvider::getClientRepository(); |
| 90 | -$clients = $client_repository->all(); | |
| 83 | +// The hidden #select-client mirror only needs the document's own client; the picker | |
| 84 | +// searches over AJAX. Loading every client here built a model per user on each open. | |
| 85 | +$clients = []; | |
| 91 | 86 | |
| 92 | 87 | // Load client data directly in PHP if quote has a client |
| 93 | 88 | $client_data = null; |
| 94 | 89 | if ($quote && $quote->getClientId()) { |
| @@ -210,212 +205,190 @@ | ||
| 210 | 205 | // $template_htmls[$template_id] = '<div class="template-not-found"><h3>Template Not Found</h3><p>The template "' . esc_html($template_id) . '" does not exist.</p></div>'; |
| 211 | 206 | // } |
| 212 | 207 | // } |
| 213 | 208 | |
| 209 | +if ( $quote_id && $quote ) { | |
| 210 | + $ei_header_display_title = $quote->title ?: $quote->number ?: __( 'Untitled quote', 'easy-invoice' ); | |
| 211 | +} else { | |
| 212 | + $ei_header_display_title = __( 'Create new quote', 'easy-invoice' ); | |
| 213 | +} | |
| 214 | + | |
| 214 | 215 | ?> |
| 215 | 216 | |
| 216 | 217 | <script> |
| 217 | -// Flag to indicate this is a quote form | |
| 218 | 218 | window.isQuoteForm = true; |
| 219 | +</script> | |
| 219 | 220 | |
| 220 | -// Handle Send Quote button | |
| 221 | -jQuery(document).ready(function($) { | |
| 222 | - $('#send-quote-btn').on('click', function(e) { | |
| 223 | - e.preventDefault(); | |
| 221 | +<script> | |
| 222 | +jQuery(function ($) { | |
| 223 | + window.easyInvoice = $.extend(true, {}, typeof window.easyInvoice === 'object' ? window.easyInvoice : {}, { | |
| 224 | + fieldConfig: <?php echo wp_json_encode( $quote_field_config ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>, | |
| 225 | + clientData: <?php echo wp_json_encode( $client_data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>, | |
| 226 | + ajaxUrl: '<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>', | |
| 227 | + nonce: '<?php echo esc_js( wp_create_nonce( 'easy_invoice_nonce' ) ); ?>', | |
| 228 | + isPro: <?php echo easy_invoice_has_pro() ? 'true' : 'false'; ?> | |
| 229 | + }); | |
| 224 | 230 | |
| 225 | - // First save the quote if it's not saved yet | |
| 226 | - if ($('#quote-id').val() == '0') { | |
| 227 | - // Quote not saved yet, save it first | |
| 228 | - $('#save-quote-btn').click(); | |
| 231 | + function t(key, fb) { | |
| 232 | + return (window.easyInvoice && easyInvoice.i18n && easyInvoice.i18n[key]) ? easyInvoice.i18n[key] : fb; | |
| 233 | + } | |
| 234 | + var spin = '<svg class="ei-btn-spinner h-4 w-4 inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>'; | |
| 235 | + var pendingSend = false; | |
| 229 | 236 | |
| 230 | - // Wait for save to complete, then send | |
| 231 | - setTimeout(function() { | |
| 232 | - if ($('#quote-id').val() != '0') { | |
| 233 | - sendQuoteEmail(); | |
| 234 | - } else { | |
| 235 | - if (typeof EasyInvoiceToast !== 'undefined') { | |
| 236 | - EasyInvoiceToast.warning('Please save the quote first before sending.'); | |
| 237 | - } else { | |
| 238 | - console.log('Please save the quote first before sending.'); | |
| 239 | - } | |
| 240 | - } | |
| 241 | - }, 2000); | |
| 242 | - } else { | |
| 243 | - // Quote already saved, send directly | |
| 237 | + $(document).on('easy-quote-saved', function (e, response) { | |
| 238 | + if (!pendingSend || !response || !response.success) { | |
| 239 | + return; | |
| 240 | + } | |
| 241 | + pendingSend = false; | |
| 242 | + if ($('#quote-id').val() !== '0') { | |
| 244 | 243 | sendQuoteEmail(); |
| 245 | 244 | } |
| 246 | 245 | }); |
| 246 | + $(document).on('easy-quote-save-failed', function () { | |
| 247 | + pendingSend = false; | |
| 248 | + }); | |
| 247 | 249 | |
| 250 | + $('#send-quote-btn').on('click', function (e) { | |
| 251 | + e.preventDefault(); | |
| 252 | + if ($('#quote-id').val() === '0') { | |
| 253 | + pendingSend = true; | |
| 254 | + $('#save-quote-btn').trigger('click'); | |
| 255 | + return; | |
| 256 | + } | |
| 257 | + sendQuoteEmail(); | |
| 258 | + }); | |
| 259 | + | |
| 248 | 260 | function sendQuoteEmail() { |
| 249 | - const quoteId = $('#quote-id').val(); | |
| 250 | - | |
| 251 | - if (quoteId == '0') { | |
| 261 | + var quoteId = $('#quote-id').val(); | |
| 262 | + if (quoteId === '0') { | |
| 252 | 263 | if (typeof EasyInvoiceToast !== 'undefined') { |
| 253 | - EasyInvoiceToast.warning('Please save the quote first before sending.'); | |
| 254 | - } else { | |
| 255 | - console.log('Please save the quote first before sending.'); | |
| 264 | + EasyInvoiceToast.warning(t('save_first_quote', 'Please save the quote before sending.')); | |
| 256 | 265 | } |
| 257 | 266 | return; |
| 258 | 267 | } |
| 268 | + var nonce = $('#send-quote-btn').data('send-nonce') || '<?php echo esc_js( wp_create_nonce( 'easy_invoice_send_quote_email' ) ); ?>'; | |
| 259 | 269 | |
| 260 | - // Use the confirmation system instead of alert | |
| 261 | - if (typeof EasyInvoiceConfirmation !== 'undefined') { | |
| 262 | - EasyInvoiceConfirmation.confirmAction('send email', 'this quote', function() { | |
| 263 | - // Show loading state | |
| 264 | - const $btn = $('#send-quote-btn'); | |
| 265 | - const originalText = $btn.html(); | |
| 266 | - $btn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin mr-2"></i>Sending...'); | |
| 267 | - | |
| 268 | - // Send AJAX request | |
| 269 | - $.ajax({ | |
| 270 | - url: window.easyInvoice.ajaxUrl, | |
| 271 | - type: 'POST', | |
| 272 | - data: { | |
| 273 | - action: 'easy_invoice_send_quote_email', | |
| 274 | - quote_id: quoteId, | |
| 275 | - nonce: '<?php echo wp_create_nonce('easy_invoice_send_quote_email'); ?>' | |
| 276 | - }, | |
| 277 | - success: function(response) { | |
| 278 | - if (response.success) { | |
| 279 | - if (typeof EasyInvoiceToast !== 'undefined') { | |
| 280 | - EasyInvoiceToast.success('Quote email sent successfully!'); | |
| 281 | - } else { | |
| 282 | - console.log('Quote email sent successfully!'); | |
| 283 | - } | |
| 284 | - } else { | |
| 285 | - if (typeof EasyInvoiceToast !== 'undefined') { | |
| 286 | - // Handle both response.data.message and response.data (direct string) | |
| 287 | - const errorMessage = (response.data && response.data.message) ? response.data.message : (response.data || 'Error sending email'); | |
| 288 | - EasyInvoiceToast.error(errorMessage); | |
| 289 | - } else { | |
| 290 | - const errorMessage = (response.data && response.data.message) ? response.data.message : (response.data || 'Unknown error'); | |
| 291 | - console.log('Error sending email: ' + errorMessage); | |
| 292 | - } | |
| 270 | + var runAjax = function () { | |
| 271 | + var $btn = $('#send-quote-btn'); | |
| 272 | + var original = $btn.html(); | |
| 273 | + $btn.prop('disabled', true).attr('aria-busy', 'true').html('<span class="inline-flex items-center gap-2">' + spin + '<span>' + t('sending', 'Sending…') + '</span></span>'); | |
| 274 | + $.ajax({ | |
| 275 | + url: window.easyInvoice.ajaxUrl, | |
| 276 | + type: 'POST', | |
| 277 | + data: { | |
| 278 | + action: 'easy_invoice_send_quote_email', | |
| 279 | + quote_id: quoteId, | |
| 280 | + nonce: nonce | |
| 281 | + }, | |
| 282 | + success: function (response) { | |
| 283 | + if (response.success) { | |
| 284 | + if (typeof EasyInvoiceToast !== 'undefined') { | |
| 285 | + EasyInvoiceToast.success(t('email_sent_quote', 'Quote email sent successfully.')); | |
| 293 | 286 | } |
| 294 | - }, | |
| 295 | - error: function() { | |
| 287 | + } else { | |
| 288 | + var msg = (response.data && response.data.message) ? response.data.message : (response.data || t('email_error', 'Error sending email.')); | |
| 296 | 289 | if (typeof EasyInvoiceToast !== 'undefined') { |
| 297 | - EasyInvoiceToast.error('Error connecting to server'); | |
| 298 | - } else { | |
| 299 | - console.log('Error connecting to server'); | |
| 290 | + EasyInvoiceToast.error(msg); | |
| 300 | 291 | } |
| 301 | - }, | |
| 302 | - complete: function() { | |
| 303 | - // Reset button | |
| 304 | - $btn.prop('disabled', false).html(originalText); | |
| 305 | 292 | } |
| 306 | - }); | |
| 293 | + }, | |
| 294 | + error: function () { | |
| 295 | + if (typeof EasyInvoiceToast !== 'undefined') { | |
| 296 | + EasyInvoiceToast.error(t('network_error', 'Error connecting to server.')); | |
| 297 | + } | |
| 298 | + }, | |
| 299 | + complete: function () { | |
| 300 | + $btn.prop('disabled', false).removeAttr('aria-busy').html(original); | |
| 301 | + } | |
| 307 | 302 | }); |
| 308 | - } else { | |
| 309 | - // Fallback to browser confirm if confirmation system not available | |
| 310 | - if (confirm('Send this quote via email?')) { | |
| 311 | - // Show loading state | |
| 312 | - const $btn = $('#send-quote-btn'); | |
| 313 | - const originalText = $btn.html(); | |
| 314 | - $btn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin mr-2"></i>Sending...'); | |
| 303 | + }; | |
| 315 | 304 | |
| 316 | - // Send AJAX request | |
| 317 | - $.ajax({ | |
| 318 | - url: window.easyInvoice.ajaxUrl, | |
| 319 | - type: 'POST', | |
| 320 | - data: { | |
| 321 | - action: 'easy_invoice_send_quote_email', | |
| 322 | - quote_id: quoteId, | |
| 323 | - nonce: '<?php echo wp_create_nonce('easy_invoice_send_quote_email'); ?>' | |
| 324 | - }, | |
| 325 | - success: function(response) { | |
| 326 | - if (response.success) { | |
| 327 | - alert('Quote email sent successfully!'); | |
| 328 | - } else { | |
| 329 | - const errorMessage = (response.data && response.data.message) ? response.data.message : (response.data || 'Error sending email'); | |
| 330 | - alert(errorMessage); | |
| 331 | - } | |
| 332 | - }, | |
| 333 | - error: function() { | |
| 334 | - alert('Error connecting to server'); | |
| 335 | - }, | |
| 336 | - complete: function() { | |
| 337 | - // Reset button | |
| 338 | - $btn.prop('disabled', false).html(originalText); | |
| 339 | - } | |
| 340 | - }); | |
| 341 | - } | |
| 305 | + if (typeof EasyInvoiceConfirmation !== 'undefined') { | |
| 306 | + EasyInvoiceConfirmation.confirmSendDocument('quote', runAjax); | |
| 307 | + } else if (window.confirm(t('send_quote_confirm_browser', 'Send this quote by email?'))) { | |
| 308 | + runAjax(); | |
| 342 | 309 | } |
| 343 | 310 | } |
| 344 | 311 | }); |
| 345 | 312 | </script> |
| 346 | 313 | |
| 347 | -<script> | |
| 348 | -// Quote field configuration and pre-loaded data | |
| 349 | -window.easyInvoice = window.easyInvoice || {}; | |
| 350 | -window.easyInvoice.fieldConfig = <?php echo json_encode($quote_field_config); ?>; | |
| 351 | -window.easyInvoice.clientData = <?php echo json_encode($client_data); ?>; | |
| 352 | -window.easyInvoice.ajaxUrl = '<?php echo admin_url('admin-ajax.php'); ?>'; | |
| 353 | -window.easyInvoice.nonce = '<?php echo wp_create_nonce('easy_invoice_nonce'); ?>'; | |
| 354 | -window.easyInvoice.isPro = <?php echo easy_invoice_has_pro() ? 'true' : 'false'; ?>; | |
| 355 | -</script> | |
| 356 | - | |
| 357 | 314 | <form id="quote-form" method="post"> |
| 358 | - <input type="hidden" id="quote-id" name="quote_id" value="<?php echo $quote_id; ?>"> | |
| 359 | - <input type="hidden" id="quote_nonce" name="quote_nonce" value="<?php echo wp_create_nonce('easy_invoice_nonce'); ?>"> | |
| 315 | + <input type="hidden" id="quote-id" name="quote_id" value="<?php echo (int) $quote_id; ?>"> | |
| 316 | + <input type="hidden" id="quote_nonce" name="quote_nonce" value="<?php echo esc_attr(wp_create_nonce('easy_invoice_nonce')); ?>"> | |
| 360 | 317 | |
| 361 | - <div id="easy-invoice-content" class="h-screen flex flex-col"> | |
| 362 | - <!-- Header --> | |
| 363 | - <div class="bg-white shadow-sm w-full z-10"> | |
| 364 | - <div class="max-w-full mx-auto px-5"> | |
| 365 | - <div class="py-3 flex items-center justify-between"> | |
| 366 | - <div class="flex items-center"> | |
| 367 | - <a href="<?php echo admin_url('admin.php?page=easy-quote-all'); ?>" | |
| 368 | - class="inline-flex items-center text-gray-600 hover:text-gray-900"> | |
| 369 | - <i class="fas fa-arrow-left mr-2"></i> | |
| 370 | - <span>Back to Quotes</span> | |
| 318 | + <div id="easy-invoice-content" class="h-screen flex flex-col bg-gray-50"> | |
| 319 | + <header class="ei-quote-builder-header ei-app-header-sync bg-white border-b border-gray-200 shadow-sm w-full z-10 box-border"> | |
| 320 | + <div class="max-w-full mx-auto h-full px-4 sm:px-5"> | |
| 321 | + <div class="ei-app-header-inner flex flex-col gap-3 py-3 sm:gap-4 lg:flex-row lg:items-center lg:justify-between lg:gap-6 lg:py-0 lg:h-full"> | |
| 322 | + <div class="ei-app-header-leading flex min-w-0 flex-1 flex-col gap-3 sm:flex-row sm:items-center sm:gap-4"> | |
| 323 | + <a href="<?php echo esc_url( admin_url( 'admin.php?page=easy-quote-all' ) ); ?>" | |
| 324 | + class="inline-flex shrink-0 items-center gap-2 self-start rounded-md text-sm font-medium text-gray-600 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"> | |
| 325 | + <svg class="h-4 w-4 shrink-0" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"> | |
| 326 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path> | |
| 327 | + </svg> | |
| 328 | + <span><?php esc_html_e( 'Back to quotes', 'easy-invoice' ); ?></span> | |
| 371 | 329 | </a> |
| 330 | + <div class="min-w-0 flex-1 border-l-0 sm:border-l sm:border-gray-200 sm:pl-4"> | |
| 331 | + <p class="text-xs font-medium uppercase tracking-wide text-gray-500"><?php esc_html_e( 'Quote', 'easy-invoice' ); ?></p> | |
| 332 | + <h1 id="ei-builder-header-title" class="mt-0.5 truncate text-lg font-semibold leading-tight text-gray-900 sm:text-xl" data-ei-default="<?php echo esc_attr( $ei_header_display_title ); ?>"> | |
| 333 | + <?php echo esc_html( $ei_header_display_title ); ?> | |
| 334 | + </h1> | |
| 335 | + <p class="sr-only"><?php esc_html_e( 'The title above matches the document title field in the editor.', 'easy-invoice' ); ?></p> | |
| 336 | + </div> | |
| 372 | 337 | </div> |
| 373 | - <h1 class="text-lg font-semibold text-gray-800"> | |
| 374 | - <?php | |
| 375 | - if ($quote_id && $quote) { | |
| 376 | - $title = $quote->title ?: $quote->number ?: 'Untitled Quote'; | |
| 377 | - echo esc_html($title); | |
| 378 | - } else { | |
| 379 | - echo 'Create New Quote'; | |
| 380 | - } | |
| 381 | - ?> | |
| 382 | - </h1> | |
| 383 | - <div class="flex items-center space-x-4"> | |
| 384 | - <button type="button" id="save-quote-btn" class="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"> | |
| 385 | - <i class="fas fa-save mr-2"></i> | |
| 386 | - <span><?php echo (isset($_GET['id']) ? __('Update Quote', 'easy-invoice') : __('Save Quote', 'easy-invoice')); ?></span> | |
| 338 | + <div class="ei-builder-header-actions flex flex-wrap items-center gap-2 lg:shrink-0"> | |
| 339 | + <button type="button" id="save-quote-btn" class="inline-flex items-center gap-2 px-3 sm: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" aria-busy="false"> | |
| 340 | + <svg class="h-4 w-4 text-gray-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"> | |
| 341 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"></path> | |
| 342 | + </svg> | |
| 343 | + <span><?php echo isset( $_GET['id'] ) ? esc_html__( 'Update quote', 'easy-invoice' ) : esc_html__( 'Save quote', 'easy-invoice' ); ?></span> | |
| 387 | 344 | </button> |
| 388 | - <button type="button" id="send-quote-btn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | |
| 389 | - <i class="fas fa-paper-plane mr-2"></i> | |
| 390 | - <span>Send Quote</span> | |
| 345 | + <button type="button" id="send-quote-btn" class="inline-flex items-center gap-2 px-3 sm:px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" data-send-nonce="<?php echo esc_attr( wp_create_nonce( 'easy_invoice_send_quote_email' ) ); ?>"> | |
| 346 | + <svg class="h-4 w-4 shrink-0" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-hidden="true"> | |
| 347 | + <path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"></path> | |
| 348 | + </svg> | |
| 349 | + <span><?php esc_html_e( 'Send quote', 'easy-invoice' ); ?></span> | |
| 391 | 350 | </button> |
| 392 | 351 | </div> |
| 393 | 352 | </div> |
| 394 | 353 | </div> |
| 395 | - </div> | |
| 354 | + </header> | |
| 396 | 355 | |
| 397 | - <!-- Main Content --> | |
| 398 | - <div class="flex-grow overflow-y-auto"> | |
| 399 | - <div class="max-w-full mx-auto px-5 py-5"> | |
| 400 | - <div id="ei-quote-builder-grid" class="grid grid-cols-1 lg:grid-cols-2 gap-8"> | |
| 401 | - <!-- Left Panel - Editable Fields --> | |
| 402 | - | |
| 403 | - <?php | |
| 404 | - include_once EASY_INVOICE_PLUGIN_DIR . 'templates/quotes/form.php'; | |
| 405 | - | |
| 406 | - include_once EASY_INVOICE_PLUGIN_DIR . 'templates/quotes/live-preview.php'; | |
| 407 | - ?> | |
| 356 | + <div class="flex-grow overflow-y-auto min-h-0"> | |
| 357 | + <div class="max-w-full mx-auto px-4 sm:px-5 py-4 sm:py-5"> | |
| 358 | + <div class="ei-builder-tabs lg:hidden flex rounded-lg bg-gray-100 p-1 gap-1 mb-4" role="tablist" aria-label="<?php echo esc_attr__( 'Editor panels', 'easy-invoice' ); ?>"> | |
| 359 | + <button type="button" role="tab" class="ei-builder-tab flex-1 rounded-md py-2 text-sm font-medium transition bg-white shadow-sm text-indigo-700" data-tab="editor" aria-selected="true"><?php esc_html_e( 'Editor', 'easy-invoice' ); ?></button> | |
| 360 | + <button type="button" role="tab" class="ei-builder-tab flex-1 rounded-md py-2 text-sm font-medium transition text-gray-600 hover:text-gray-800" data-tab="preview" aria-selected="false"><?php esc_html_e( 'Preview', 'easy-invoice' ); ?></button> | |
| 408 | 361 | </div> |
| 362 | + <div id="ei-builder-shell-hint" class="ei-builder-shell-hint mb-4 hidden rounded-md border border-indigo-100 bg-indigo-50/90 p-3 text-sm text-gray-700 shadow-sm lg:hidden" role="status" hidden> | |
| 363 | + <div class="flex items-start gap-3"> | |
| 364 | + <span class="mt-0.5 inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-indigo-100 text-indigo-700" aria-hidden="true"> | |
| 365 | + <svg class="h-3.5 w-3.5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg> | |
| 366 | + </span> | |
| 367 | + <p class="min-w-0 flex-1 leading-snug"><?php esc_html_e( 'On smaller screens, use the tabs to switch between the editor and the live preview.', 'easy-invoice' ); ?></p> | |
| 368 | + <button type="button" id="ei-builder-shell-hint-dismiss" class="shrink-0 rounded-md px-2 py-1 text-xs font-medium text-indigo-700 hover:bg-indigo-100 focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
| 369 | + <?php esc_html_e( 'Got it', 'easy-invoice' ); ?> | |
| 370 | + </button> | |
| 371 | + </div> | |
| 372 | + </div> | |
| 373 | + <div id="ei-quote-builder-grid" class="grid grid-cols-1 lg:grid-cols-2 gap-6 lg:gap-8"> | |
| 374 | + <div id="ei-builder-editor" class="min-w-0 ei-builder-panel-editor"> | |
| 375 | + <?php include_once EASY_INVOICE_PLUGIN_DIR . 'templates/quotes/form.php'; ?> | |
| 376 | + </div> | |
| 377 | + <div id="ei-builder-preview" class="min-w-0 ei-builder-panel-preview hidden lg:block"> | |
| 378 | + <?php include_once EASY_INVOICE_PLUGIN_DIR . 'templates/quotes/live-preview.php'; ?> | |
| 379 | + </div> | |
| 380 | + </div> | |
| 409 | 381 | |
| 410 | - <!-- Add Client Modal --> | |
| 411 | - <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"> | |
| 382 | + <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" role="dialog" aria-modal="true" aria-labelledby="ei-quote-add-client-title"> | |
| 412 | 383 | <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"> |
| 413 | 384 | <div class="flex justify-between items-center mb-4"> |
| 414 | - <h3 class="text-lg font-medium text-gray-900">Add New Client</h3> | |
| 415 | - <button type="button" class="close-modal text-gray-400 hover:text-gray-500"> | |
| 416 | - <span class="sr-only">Close</span> | |
| 417 | - <i class="fas fa-times"></i> | |
| 385 | + <h3 id="ei-quote-add-client-title" class="text-lg font-medium text-gray-900"><?php esc_html_e( 'Add new client', 'easy-invoice' ); ?></h3> | |
| 386 | + <button type="button" class="close-modal rounded-md p-1 text-gray-400 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
| 387 | + <span class="sr-only"><?php esc_html_e( 'Close', 'easy-invoice' ); ?></span> | |
| 388 | + <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"> | |
| 389 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> | |
| 390 | + </svg> | |
| 418 | 391 | </button> |
| 419 | 392 | </div> |
| 420 | 393 | |
| 421 | 394 | <?php |