PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.1.2
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.1.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 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / templates / settings-page.php

settings-page.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.1.2, at templates/settings-page.php

1,215 lines 107.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Settings page template for Easy Invoice
4 *
5 * @package Easy_Invoice
6 * @since 1.0.0
7 */
8
9 // Exit if accessed directly
10 if (!defined('ABSPATH')) {
11 exit;
12 }
13
14 // Function to dequeue WordPress admin styles and load our custom styles
15 function easy_invoice_dequeue_admin_styles() {
16 // Get current screen
17 $screen = get_current_screen();
18
19 // Check if we're on an Easy Invoice page
20 if ($screen && property_exists($screen, 'id') && strpos($screen->id, 'easy-invoice') !== false) {
21 // Remove all WordPress admin styles
22 global $wp_styles;
23
24 $styles_to_remove = [
25 'wp-admin', 'admin-bar', 'colors', 'ie', 'common', 'forms', 'admin-menu',
26 'dashboard', 'list-tables', 'edit', 'revisions', 'media', 'themes', 'about',
27 'nav-menus', 'widgets', 'site-icon', 'l10n', 'code-editor', 'wp-auth-check',
28 'dashicons', 'wp-pointer', 'buttons', 'wp-jquery-ui-dialog', 'wp-color-picker',
29 'wp-components', 'wp-edit-post', 'wp-format-library', 'wp-block-library',
30 'wp-block-library-theme', 'wp-nux', 'wp-reusable-blocks', 'wp-editor',
31 'wp-edit-blocks', 'wp-block-editor',
32 ];
33 $styles_to_remove = array_unique($styles_to_remove); // Ensure no duplicates
34
35 foreach ($styles_to_remove as $style) {
36 if (isset($wp_styles->registered[$style])) {
37 wp_dequeue_style($style);
38 wp_deregister_style($style);
39 }
40 }
41 add_filter('show_admin_bar', '__return_false');
42
43 // Add custom CSS to hide screen-meta-links
44 echo '<style>
45 .screen-meta-links,
46 #screen-meta,
47 #screen-meta-links {
48 display: none !important;
49 }
50 </style>';
51 }
52 }
53 add_action('admin_enqueue_scripts', 'easy_invoice_dequeue_admin_styles', 9999);
54
55 /**
56 * Helper function to render a single settings field.
57 *
58 * @param string $option_key The option key.
59 * @param array $field_config The field configuration.
60 * @param mixed $current_value The current value of the field.
61 */
62 function easy_invoice_render_field($option_key, $field_config, $current_value) {
63 $label = esc_html($field_config['label'] ?? '');
64 $type = $field_config['type'] ?? 'text';
65 $default = $field_config['default'] ?? '';
66 $value = $current_value ?? $default;
67 $description_text = $field_config['description'] ?? '';
68 $field_id = esc_attr($option_key);
69 $field_name = 'settings[' . esc_attr($option_key) . ']';
70 $col_span_class = esc_attr($field_config['col_span'] ?? 'sm:col-span-6');
71 $placeholder = isset($field_config['placeholder']) ? esc_attr($field_config['placeholder']) : '';
72 $required = !empty($field_config['required']) ? 'required' : '';
73 $aria_describedby = !empty($description_text) ? 'aria-describedby="' . $field_id . '-description"' : '';
74
75 echo '<div class=" ' . $col_span_class . '">'; // Added 'form-group' class for JS show/hide functionality
76
77 // Special layout for checkboxes with descriptions (e.g., Invoice Numbering type)
78 if ($type === 'checkbox' && !empty($description_text) && ($option_key === 'easy_invoice_invoice_numbering')) {
79 echo '<div class="flex items-start space-x-3 p-4 bg-gray-50 rounded-lg border border-gray-200">';
80 echo ' <div class="flex items-center h-5 mt-0.5">';
81 echo ' <input type="checkbox" name="' . $field_name . '" id="' . $field_id . '" value="yes" ' . checked($value, 'yes', false) . ' class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" ' . $required . '>';
82 echo ' </div>';
83 echo ' <div class="flex-1">';
84 echo ' <label for="' . $field_id . '" class="block text-sm font-medium text-gray-700 mb-1">' . $label . '</label>';
85 echo ' <p id="' . $field_id . '-description" class="text-sm text-gray-400 leading-relaxed">' . wp_kses($description_text, array(
86 'a' => array(
87 'href' => array(),
88 'target' => array(),
89 'class' => array()
90 )
91 )) . '</p>';
92 echo ' </div>';
93 echo '</div>';
94 } else {
95 // Standard label for other types, or checkboxes without the special description structure
96 if ($type !== 'checkbox') {
97 echo '<label for="' . $field_id . '" class="block text-sm font-medium text-gray-700 mb-2">' . $label . '</label>';
98 }
99
100 $input_class = 'mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm';
101 $select_class = 'mt-1 block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md';
102
103
104 switch ($type) {
105 case 'text':
106 case 'email':
107 case 'url':
108 case 'tel':
109 echo '<input type="' . esc_attr($type) . '" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($value) . '" class="' . $input_class . '" placeholder="' . $placeholder . '" ' . $required . ' ' . $aria_describedby . '>';
110 break;
111 case 'number':
112 $min = isset($field_config['min']) ? ' min="' . esc_attr($field_config['min']) . '"' : '';
113 $max = isset($field_config['max']) ? ' max="' . esc_attr($field_config['max']) . '"' : '';
114 $step = isset($field_config['step']) ? ' step="' . esc_attr($field_config['step']) . '"' : '';
115 echo '<input type="number" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($value) . '"' . $min . $max . $step . ' class="' . $input_class . '" placeholder="' . $placeholder . '" ' . $required . ' ' . $aria_describedby . '>';
116 break;
117 case 'textarea':
118 echo '<textarea id="' . $field_id . '" name="' . $field_name . '" rows="3" class="' . $input_class . '" placeholder="' . $placeholder . '" ' . $required . ' ' . $aria_describedby . '>' . wp_kses_post($value) . '</textarea>';
119 break;
120 case 'select':
121 echo '<select id="' . $field_id . '" name="' . $field_name . '" class="' . $select_class . '" ' . $required . ' ' . $aria_describedby . '>';
122 if (!empty($field_config['options']) && is_array($field_config['options'])) {
123 foreach ($field_config['options'] as $opt_val => $opt_label) {
124 echo '<option value="' . esc_attr($opt_val) . '" ' . selected($value, $opt_val, false) . '>' . esc_html($opt_label) . '</option>';
125 }
126 }
127 echo '</select>';
128 break;
129 case 'multiselect':
130 echo '<select id="' . $field_id . '" name="' . $field_name . '[]" class="' . $select_class . '" multiple="multiple" aria-label="' . esc_attr($label) . '">';
131 if (!empty($field_config['options']) && is_array($field_config['options'])) {
132 foreach ($field_config['options'] as $opt_val => $opt_label) {
133 $selected = is_array($value) && in_array($opt_val, $value) ? 'selected="selected"' : '';
134 echo '<option value="' . esc_attr($opt_val) . '" ' . $selected . '>' . esc_html($opt_label) . '</option>';
135 }
136 }
137 echo '</select>';
138 break;
139 case 'readonly':
140 echo '<input type="text" id="' . $field_id . '" value="' . esc_attr($value) . '" class="' . $input_class . ' bg-gray-50" readonly ' . $aria_describedby . '>';
141 echo '<input type="hidden" name="' . $field_name . '" value="' . esc_attr($value) . '">';
142 break;
143 case 'checkbox': // Checkboxes that don't use the special description layout
144 echo '<div class="flex items-center">'; // Original 'tax_enabled' was just this simple structure
145 echo ' <input type="checkbox" name="' . $field_name . '" id="' . $field_id . '" value="yes" ' . checked($value, 'yes', false) . ' class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" ' . $required . ' ' . $aria_describedby . '>';
146 echo ' <label for="' . $field_id . '" class="ml-2 block text-sm text-gray-900">' . $label . '</label>';
147 echo '</div>';
148 if (!empty($description_text)) { // Show description below the field
149 echo '<p id="' . $field_id . '-description" class="mt-2 text-sm text-gray-400 leading-relaxed">' . wp_kses($description_text, array(
150 'a' => array(
151 'href' => array(),
152 'target' => array(),
153 'class' => array()
154 )
155 )) . '</p>';
156 }
157 break;
158 case 'image':
159 echo '<div class="mt-1 flex items-center">';
160 echo ' <span class="inline-block h-12 w-12 rounded-full overflow-hidden bg-gray-100">';
161 echo ' <img id="' . $field_id . '-preview" src="' . esc_url($value) . '" alt="' . esc_attr($label) . '" class="h-full w-full object-cover' . (empty($value) ? ' hidden' : '') . '">';
162 echo ' </span>';
163 echo ' <button type="button" id="upload_image_button_' . $field_id . '" class="upload-logo-button ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" data-uploader_title="' . esc_attr__('Choose Logo', 'easy-invoice') . '" data-uploader_button_text="' . esc_attr__('Select Logo', 'easy-invoice') . '">';
164 echo esc_html__('Change', 'easy-invoice');
165 echo '</button>';
166 echo ' <input type="hidden" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($value) . '" ' . $required . ' ' . $aria_describedby . '>';
167 echo '</div>';
168 break;
169 case 'wp_editor':
170 // For wp_editor, the label is usually handled before calling it.
171 // The col_span div wraps this.
172 wp_editor( $value, $field_id, ['textarea_name' => $field_name, 'teeny' => true, 'media_buttons' => false, 'textarea_rows' => 7, 'editor_class' => 'mt-1'] );
173 // Handle description for wp_editor specifically
174 if (!empty($description_text)) {
175 echo '<p id="' . $field_id . '-description" class="mt-1 text-sm text-gray-400">' . wp_kses($description_text, array(
176 'a' => array(
177 'href' => array(),
178 'target' => array(),
179 'class' => array()
180 )
181 )) . '</p>';
182 }
183 break;
184 case 'test_email':
185 echo '<div class="mt-1">';
186 echo ' <div class="flex items-center space-x-3">';
187 echo ' <input type="email" id="' . $field_id . '_email" placeholder="' . esc_attr__('Enter email address to test', 'easy-invoice') . '" class="mt-1 block w-[200px] border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="' . esc_attr(get_option('admin_email')) . '">';
188 echo ' <button type="button" id="' . $field_id . '_button" class="test-email-button inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 h-[38px]">';
189 echo ' <i class="fas fa-paper-plane mr-2" aria-hidden="true"></i>';
190 echo ' ' . esc_html__('Send Test Email', 'easy-invoice');
191 echo ' </button>';
192 echo ' </div>';
193 echo ' <div id="' . $field_id . '_result" class="mt-2" style="display: none;"></div>';
194 echo '</div>';
195 break;
196 case 'color':
197 echo '<input type="color" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($value) . '" class="h-10 w-20 border border-gray-300 rounded">';
198 break;
199 case 'range':
200 $min = isset($field_config['min']) ? ' min="' . esc_attr($field_config['min']) . '"' : '';
201 $max = isset($field_config['max']) ? ' max="' . esc_attr($field_config['max']) . '"' : '';
202 $step = isset($field_config['step']) ? ' step="' . esc_attr($field_config['step']) . '"' : '';
203 echo '<input type="range" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($value) . '"' . $min . $max . $step . ' class="w-full">';
204 echo '<span class="text-sm text-gray-400">' . esc_html($value) . '%</span>';
205 break;
206 case 'button':
207 $button_text = $field_config['button_text'] ?? $label;
208 $button_class = $field_config['button_class'] ?? 'button button-secondary';
209 $button_class_name = 'regenerate-invoice-numbers-button';
210 if ($option_key === 'easy_invoice_regenerate_quote_numbers') {
211 $button_class_name = 'regenerate-quote-numbers-button';
212 }
213 echo '<button type="button" id="' . $field_id . '" class="' . esc_attr($button_class) . ' ' . $button_class_name . '">';
214 echo esc_html($button_text);
215 echo '</button>';
216 break;
217 case 'image_upload':
218 echo '<div class="image-upload-wrap">';
219 echo '<div class="flex items-center space-x-3">';
220 echo ' <input type="hidden" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($value) . '">';
221 echo ' <button type="button" class="upload-image-button bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" data-target="' . $field_id . '">';
222 echo esc_html__('Upload Image', 'easy-invoice');
223 echo '</button>';
224 echo '</div>';
225 echo '<div class="image-preview mt-2' . (empty($value) ? ' hidden' : '') . '">';
226 if (!empty($value)) {
227 echo '<img src="' . esc_url($value) . '" alt="Preview" style="max-width: 200px; max-height: 100px;">';
228 }
229 echo '</div>';
230 echo '</div>';
231 break;
232 default:
233 // Special handling for regenerate invoice numbers button
234 if ($option_key === 'easy_invoice_regenerate_invoice_numbers') {
235 $button_text = $field_config['button_text'] ?? $label;
236 $button_class = $field_config['button_class'] ?? 'button button-secondary';
237 echo '<button type="button" id="' . $field_id . '" class="' . esc_attr($button_class) . ' regenerate-invoice-numbers-button">';
238 echo esc_html($button_text);
239 echo '</button>';
240 } elseif ($option_key === 'easy_invoice_regenerate_quote_numbers') {
241 $button_text = $field_config['button_text'] ?? $label;
242 $button_class = $field_config['button_class'] ?? 'button button-secondary';
243 echo '<button type="button" id="' . $field_id . '" class="' . esc_attr($button_class) . ' regenerate-quote-numbers-button">';
244 echo esc_html($button_text);
245 echo '</button>';
246 } else {
247 echo '<input type="text" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($value) . '" class="' . $input_class . '" placeholder="' . $placeholder . '" ' . $required . ' ' . $aria_describedby . '>';
248 }
249 break;
250 }
251 // General description for non-checkbox and non-wp_editor fields (if it exists and not handled by special layouts)
252 if ($type !== 'checkbox' && $type !== 'wp_editor' && !empty($description_text)) {
253 echo '<p id="' . $field_id . '-description" class="mt-1 text-sm text-gray-400">' . wp_kses($description_text, array(
254 'a' => array(
255 'href' => array(),
256 'target' => array(),
257 'class' => array()
258 )
259 )) . '</p>';
260 }
261 }
262 echo '</div>'; // End col_span div
263 }
264
265 /**
266 * Filter settings config to allow extensions to modify it
267 */
268 $settings_config = apply_filters('easy_invoice_settings_fields_config', $settings_config);
269
270 $active_section = $_GET['section'] ?? key($settings_config);
271 if (!isset($settings_config[$active_section])) {
272 $active_section = key($settings_config);
273 }
274 ?>
275
276 <div class="p-8">
277 <!-- Header with Actions -->
278 <div class="bg-white border-b border-gray-200 px-6 py-5" style="margin-left: -2rem; margin-top: -2rem; padding-left:2rem; padding-right:2rem; margin-right: -2rem;">
279 <div class="flex items-center justify-between">
280 <div>
281 <h1 class="text-2xl font-bold text-gray-900"><?php esc_html_e('Settings', 'easy-invoice'); ?></h1>
282 <p class="mt-1 text-sm text-gray-400"><?php esc_html_e('Configure your invoice management system', 'easy-invoice'); ?></p>
283 </div>
284 <div class="flex items-center space-x-3">
285 <button type="button" id="save-settings" 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" aria-label="<?php esc_attr_e('Save Settings', 'easy-invoice'); ?>">
286 <i class="fas fa-save mr-2" aria-hidden="true"></i>
287 <?php esc_html_e('Save Changes', 'easy-invoice'); ?>
288 </button>
289 </div>
290 </div>
291 </div>
292
293 <!-- Main Content -->
294 <div class="max-w-7xl mx-auto">
295 <div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mt-8">
296 <!-- Left Sidebar - Navigation -->
297 <div class="lg:col-span-1">
298 <div class="bg-white shadow-lg rounded-lg sticky top-24 border border-gray-200">
299 <nav class="space-y-2 p-4" aria-label="<?php esc_attr_e('Settings Navigation', 'easy-invoice'); ?>">
300 <?php foreach ($settings_config as $section_id => $section_data) : ?>
301 <div class="space-y-1">
302 <a href="?page=easy-invoice-settings&section=<?php echo esc_attr($section_id); ?>"
303 class="nav-link group flex items-center px-4 py-3 text-sm font-medium rounded-lg transition-all duration-200 <?php echo ($section_id === $active_section) ? 'bg-indigo-50 text-indigo-700 border-r-2 border-indigo-600' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>"
304 aria-selected="<?php echo $section_id === $active_section ? 'true' : 'false'; ?>"
305 role="tab">
306
307 <?php if ($section_id === 'company'): ?>
308 <svg class="<?php echo ($section_id === $active_section) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-gray-500'; ?> mr-3 flex-shrink-0 h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
309 <path d="M3 9L12 2L21 9V20C21 20.5304 20.7893 21.0391 20.4142 21.4142C20.0391 21.7893 19.5304 22 19 22H5C4.46957 22 3.96086 21.7893 3.58579 21.4142C3.21071 21.0391 3 20.5304 3 20V9Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
310 <polyline points="9,22 9,12 15,12 15,22" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
311 </svg>
312 <?php elseif ($section_id === 'invoice'): ?>
313 <svg class="<?php echo ($section_id === $active_section) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-gray-500'; ?> mr-3 flex-shrink-0 h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
314 <path d="M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
315 <path d="M14 2V8H20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
316 <path d="M16 13H8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
317 <path d="M16 17H8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
318 <path d="M10 9H8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
319 </svg>
320 <?php elseif ($section_id === 'quote'): ?>
321 <svg class="<?php echo ($section_id === $active_section) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-gray-500'; ?> mr-3 flex-shrink-0 h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
322 <path d="M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
323 <path d="M14 2V8H20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
324 <path d="M9 9H15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
325 <path d="M9 13H15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
326 <path d="M9 17H13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
327 </svg>
328 <?php elseif ($section_id === 'currency'): ?>
329 <svg class="<?php echo ($section_id === $active_section) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-gray-500'; ?> mr-3 flex-shrink-0 h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
330 <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
331 <path d="M12 1V23" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
332 <path d="M17 5H9.5C8.57174 5 7.6815 5.36875 7.02513 6.02513C6.36875 6.6815 6 7.57174 6 8.5C6 9.42826 6.36875 10.3185 7.02513 10.9749C7.6815 11.6313 8.57174 12 9.5 12H14.5C15.4283 12 16.3185 12.3687 16.9749 13.0251C17.6313 13.6815 18 14.5717 18 15.5C18 16.4283 17.6313 17.3185 16.9749 17.9749C16.3185 18.6313 15.4283 19 14.5 19H6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
333 </svg>
334 <?php elseif ($section_id === 'tax'): ?>
335 <svg class="<?php echo ($section_id === $active_section) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-gray-500'; ?> mr-3 flex-shrink-0 h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
336 <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
337 <path d="M8 14L16 8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
338 <path d="M8 8L16 14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
339 <path d="M12 6V18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
340 </svg>
341 <?php elseif ($section_id === 'payment'): ?>
342 <svg class="<?php echo ($section_id === $active_section) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-gray-500'; ?> mr-3 flex-shrink-0 h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
343 <rect x="1" y="4" width="22" height="16" rx="2" ry="2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
344 <line x1="1" y1="10" x2="23" y2="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
345 </svg>
346 <?php elseif ($section_id === 'email'): ?>
347 <svg class="<?php echo ($section_id === $active_section) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-gray-500'; ?> mr-3 flex-shrink-0 h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
348 <path d="M4 4H20C21.1 4 22 4.9 22 6V18C22 19.1 21.1 20 20 20H4C2.9 20 2 19.1 2 18V6C2 4.9 2.9 4 4 4Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
349 <polyline points="22,6 12,13 2,6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
350 </svg>
351 <?php elseif ($section_id === 'advanced'): ?>
352 <svg class="<?php echo ($section_id === $active_section) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-gray-500'; ?> mr-3 flex-shrink-0 h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
353 <circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
354 <path d="M19.4 15A1.65 1.65 0 0 0 18 14A6 6 0 0 0 6 14A1.65 1.65 0 0 0 4.6 15A2 2 0 0 0 6 19H18A2 2 0 0 0 19.4 15Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
355 </svg>
356 <?php elseif ($section_id === 'text_settings'): ?>
357 <svg class="<?php echo ($section_id === $active_section) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-gray-500'; ?> mr-3 flex-shrink-0 h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
358 <path d="M4 7V4C4 3.44772 4.44772 3 5 3H19C19.5523 3 20 3.44772 20 4V7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
359 <path d="M4 7V20C4 20.5523 4.44772 21 5 21H19C19.5523 21 20 20.5523 20 20V7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
360 <path d="M8 11H16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
361 <path d="M8 15H12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
362 <path d="M8 19H14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
363 <path d="M12 11L14 13L12 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
364 </svg>
365 <?php else: ?>
366 <svg class="<?php echo ($section_id === $active_section) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-gray-500'; ?> mr-3 flex-shrink-0 h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
367 <circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
368 <path d="M19.4 15A1.65 1.65 0 0 0 18 14A6 6 0 0 0 6 14A1.65 1.65 0 0 0 4.6 15A2 2 0 0 0 6 19H18A2 2 0 0 0 19.4 15Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
369 </svg>
370 <?php endif; ?>
371
372 <span class="flex-1"><?php echo esc_html($section_data['title']); ?></span>
373 <?php if ($section_id === 'email' && !empty($section_data['subsections'])): ?>
374 <svg class="flex-shrink-0 h-4 w-4 text-gray-400 group-hover:text-gray-500 transition-transform duration-200" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
375 <polyline points="6,9 12,15 18,9" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
376 </svg>
377 <?php endif; ?>
378 </a>
379
380 <?php if ($section_id === 'email' && !empty($section_data['subsections'])): ?>
381 <div class="ml-6 space-y-1 email-settings-submenu" style="display: none;">
382 <?php
383 // Only show active subsection if we're actually on an email settings page
384 $active_email_subsection = null;
385 if ($active_section === 'email') {
386 if (isset($_GET['subsection']) && isset($section_data['subsections'][$_GET['subsection']])) {
387 $active_email_subsection = $_GET['subsection'];
388 } elseif (!empty($_GET['subsection']) && isset($section_data['subsections'][$_GET['subsection']])) {
389 $active_email_subsection = $_GET['subsection'];
390 } else {
391 // Default to 'general' only if we're on email section but no specific subsection
392 $active_email_subsection = 'general';
393 }
394 }
395 ?>
396 <?php foreach ($section_data['subsections'] as $subsection_id => $subsection_data): ?>
397 <?php $is_active_subsection = ($active_email_subsection && $subsection_id === $active_email_subsection); ?>
398 <a href="?page=easy-invoice-settings&section=email&subsection=<?php echo esc_attr($subsection_id); ?>"
399 class="email-submenu-link group flex items-center px-3 py-2 text-sm font-medium rounded-md transition-all duration-200 <?php echo $is_active_subsection ? 'text-indigo-700 bg-indigo-50 border-l-2 border-indigo-500' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'; ?>"
400 data-subsection="<?php echo esc_attr($subsection_id); ?>"
401 data-section="<?php echo esc_attr($section_id); ?>">
402 <svg class="mr-2 flex-shrink-0 h-4 w-4 <?php echo $is_active_subsection ? 'text-indigo-500' : 'text-gray-400 group-hover:text-gray-500'; ?>" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
403 <polyline points="9,18 15,12 9,6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
404 </svg>
405 <?php
406 // Display shorter names for better UX
407 $display_name = '';
408 switch ($subsection_id) {
409 case 'general':
410 $display_name = 'General';
411 break;
412 case 'invoice_available':
413 $display_name = 'Invoice Available';
414 break;
415 case 'quote_available':
416 $display_name = 'Quote Available';
417 break;
418 case 'payment_received':
419 $display_name = 'Payment Received';
420 break;
421 default:
422 $display_name = $subsection_data['title'];
423 break;
424 }
425 echo esc_html($display_name);
426 ?>
427 </a>
428 <?php endforeach; ?>
429 </div>
430 <?php endif; ?>
431 </div>
432 <?php endforeach; ?>
433 </nav>
434 </div>
435 </div>
436
437 <!-- Right Content - Settings -->
438 <div class="lg:col-span-2">
439 <form id="settings-form" method="post" action="">
440 <?php wp_nonce_field('easy_invoice_settings', 'easy_invoice_settings_nonce'); ?>
441 <input type="hidden" name="action" value="easy_invoice_save_settings">
442
443 <?php foreach ($settings_config as $section_id => $section_data) : ?>
444 <div id="<?php echo esc_attr($section_id); ?>"
445 class="settings-section bg-white shadow rounded-lg <?php echo ($section_id !== $active_section) ? 'hidden' : ''; ?>"
446 role="tabpanel"
447 aria-labelledby="tab-<?php echo esc_attr($section_id); ?>">
448 <div class="px-6 py-5 border-b border-gray-200">
449 <div class="flex items-center">
450 <div class="flex-shrink-0 bg-indigo-100 rounded-lg p-3">
451 <?php if (isset($section_data['icon']) && !empty($section_data['icon'])): ?>
452 <?php if (strpos($section_data['icon'], '<svg') === 0): ?>
453 <?php echo $section_data['icon']; ?>
454 <?php else: ?>
455 <i class="<?php echo esc_attr($section_data['icon']); ?> text-indigo-600 text-xl"></i>
456 <?php endif; ?>
457 <?php else: ?>
458 <svg class="text-indigo-600 text-xl h-6 w-6" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
459 <circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
460 <path d="M19.4 15A1.65 1.65 0 0 0 18 14A6 6 0 0 0 6 14A1.65 1.65 0 0 0 4.6 15A2 2 0 0 0 6 19H18A2 2 0 0 0 19.4 15Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
461 </svg>
462 <?php endif; ?>
463 </div>
464 <div class="ml-4">
465 <h2 class="text-lg font-medium text-gray-900"><?php echo esc_html($section_data['title']); ?></h2>
466 <p class="mt-1 text-sm text-gray-400"><?php echo esc_html($section_data['description']); ?></p>
467 </div>
468 </div>
469 </div>
470 <div class="px-6 py-5">
471 <div class="email-settings">
472 <?php if ($section_id === 'email' && !empty($section_data['subsections'])): ?>
473 <?php
474 // Determine the active subsection from the hash or default to 'general'
475 $active_email_subsection = 'general';
476 if (isset($_GET['subsection']) && isset($section_data['subsections'][$_GET['subsection']])) {
477 $active_email_subsection = $_GET['subsection'];
478 } elseif (!empty($_GET['subsection']) && isset($section_data['subsections'][$_GET['subsection']])) {
479 $active_email_subsection = $_GET['subsection'];
480 }
481 if (!isset($section_data['subsections'][$active_email_subsection])) {
482 $active_email_subsection = array_key_first($section_data['subsections']);
483 }
484 $subsection_data = $section_data['subsections'][$active_email_subsection];
485 ?>
486 <?php
487 // Render all email subsections so JS can show/hide them
488 foreach ($section_data['subsections'] as $subsection_key => $subsection_data) {
489 $is_active = ($subsection_key === $active_email_subsection) ? '' : 'hidden';
490 ?>
491 <div id="email--<?php echo esc_attr($subsection_key); ?>-content" class="email-subsection-content <?php echo $is_active; ?>">
492 <?php
493 // Display subsection title
494 $subsection_title = '';
495 switch ($subsection_key) {
496 case 'general':
497 $subsection_title = 'General Email Settings';
498 break;
499 case 'invoice_available':
500 $subsection_title = 'Invoice Available Email Settings';
501 break;
502 case 'quote_available':
503 $subsection_title = 'Quote Available Email Settings';
504 break;
505 case 'payment_received':
506 $subsection_title = 'Payment Received Email Settings';
507 break;
508 default:
509 $subsection_title = $subsection_data['title'];
510 break;
511 }
512 ?>
513 <div class="mb-6">
514 <h3 class="text-lg font-medium text-gray-900 mb-2"><?php echo esc_html($subsection_title); ?></h3>
515 <?php if (!empty($subsection_data['description'])): ?>
516 <p class="text-sm text-gray-400"><?php echo esc_html($subsection_data['description']); ?></p>
517 <?php endif; ?>
518 </div>
519 <div class="space-y-6">
520 <?php if (!empty($subsection_data['fields'])) {
521 foreach ($subsection_data['fields'] as $option_key => $field_config) {
522 $current_value = $settings[$option_key] ?? ($field_config['default'] ?? '');
523 $field_id = esc_attr($option_key);
524 $field_type = $field_config['type'] ?? 'text';
525 $field_label = esc_html($field_config['label'] ?? '');
526 $field_placeholder = isset($field_config['placeholder']) ? esc_attr($field_config['placeholder']) : '';
527 $field_description = $field_config['description'] ?? '';
528 $field_name = 'settings[' . esc_attr($option_key) . ']';
529 $input_class = 'mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm';
530 $select_class = 'mt-1 block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md';
531 $col_span_class = esc_attr($field_config['col_span'] ?? 'sm:col-span-6');
532 echo '<div class="field-wrapper form-group">';
533 if ($field_type !== 'checkbox') {
534 echo '<label for="' . $field_id . '" class="block text-sm font-medium text-gray-700">' . $field_label . '</label>';
535 }
536
537 // Define required and aria_describedby variables
538 $required = !empty($field_config['required']) ? 'required' : '';
539 $aria_describedby = !empty($field_description) ? 'aria-describedby="' . $field_id . '-description"' : '';
540
541 switch ($field_type) {
542 case 'text':
543 case 'email':
544 case 'url':
545 case 'tel':
546 echo '<input type="' . esc_attr($field_type) . '" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '" class="' . $input_class . '" placeholder="' . $field_placeholder . '" ' . $required . ' ' . $aria_describedby . '>';
547 break;
548 case 'number':
549 $min = isset($field_config['min']) ? ' min="' . esc_attr($field_config['min']) . '"' : '';
550 $max = isset($field_config['max']) ? ' max="' . esc_attr($field_config['max']) . '"' : '';
551 $step = isset($field_config['step']) ? ' step="' . esc_attr($field_config['step']) . '"' : '';
552 echo '<input type="number" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '"' . $min . $max . $step . ' class="' . $input_class . '" placeholder="' . $field_placeholder . '" ' . $required . ' ' . $aria_describedby . '>';
553 break;
554 case 'textarea':
555 echo '<textarea id="' . $field_id . '" name="' . $field_name . '" rows="3" class="' . $input_class . '" placeholder="' . $field_placeholder . '" ' . $required . ' ' . $aria_describedby . '>' . wp_kses_post($current_value) . '</textarea>';
556 break;
557 case 'select':
558 echo '<select id="' . $field_id . '" name="' . $field_name . '" class="' . $select_class . '" ' . $required . ' ' . $aria_describedby . '>';
559 if (!empty($field_config['options']) && is_array($field_config['options'])) {
560 foreach ($field_config['options'] as $opt_val => $opt_label) {
561 echo '<option value="' . esc_attr($opt_val) . '" ' . selected($current_value, $opt_val, false) . '>' . esc_html($opt_label) . '</option>';
562 }
563 }
564 echo '</select>';
565 break;
566 case 'multiselect':
567 echo '<select id="' . $field_id . '" name="' . $field_name . '[]" class="' . $select_class . '" multiple="multiple" aria-label="' . esc_attr($field_label) . '">';
568 if (!empty($field_config['options']) && is_array($field_config['options'])) {
569 foreach ($field_config['options'] as $opt_val => $opt_label) {
570 $selected = is_array($current_value) && in_array($opt_val, $current_value) ? 'selected="selected"' : '';
571 echo '<option value="' . esc_attr($opt_val) . '" ' . $selected . '>' . esc_html($opt_label) . '</option>';
572 }
573 }
574 echo '</select>';
575 break;
576 case 'readonly':
577 echo '<input type="text" id="' . $field_id . '" value="' . esc_attr($current_value) . '" class="' . $input_class . ' bg-gray-50" readonly>';
578 echo '<input type="hidden" name="' . $field_name . '" value="' . esc_attr($current_value) . '">';
579 break;
580 case 'checkbox':
581 echo '<div class="flex items-center">';
582 echo ' <input type="checkbox" name="' . $field_name . '" id="' . $field_id . '" value="yes" ' . checked($current_value, 'yes', false) . ' class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">';
583 echo ' <label for="' . $field_id . '" class="ml-2 block text-sm text-gray-900">' . $field_label . '</label>';
584 echo '</div>';
585 if (!empty($field_description)) {
586 echo '<p id="' . $field_id . '-description" class="mt-2 text-sm text-gray-400 leading-relaxed">' . esc_html($field_description) . '</p>';
587 }
588 break;
589 case 'image':
590 echo '<div class="mt-1 flex items-center">';
591 echo ' <span class="inline-block h-12 w-12 rounded-full overflow-hidden bg-gray-100">';
592 echo ' <img id="' . $field_id . '-preview" src="' . esc_url($current_value) . '" alt="' . esc_attr($field_label) . '" class="h-full w-full object-cover' . (empty($current_value) ? ' hidden' : '') . '">';
593 echo ' </span>';
594 echo ' <button type="button" id="upload_image_button_' . $field_id . '" class="upload-logo-button ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" data-uploader_title="' . esc_attr__('Choose Logo', 'easy-invoice') . '" data-uploader_button_text="' . esc_attr__('Select Logo', 'easy-invoice') . '">';
595 echo esc_html__('Change', 'easy-invoice');
596 echo '</button>';
597 echo ' <input type="hidden" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '">';
598 echo '</div>';
599 break;
600 case 'wp_editor':
601 wp_editor( $current_value, $field_id, ['textarea_name' => $field_name, 'teeny' => true, 'media_buttons' => false, 'textarea_rows' => 7, 'editor_class' => 'mt-1'] );
602 // Handle description for wp_editor specifically
603 if (!empty($field_description)) {
604 echo '<p class="mt-1 text-sm text-gray-400">' . esc_html($field_description) . '</p>';
605 }
606 break;
607 case 'test_email':
608 echo '<div class="mt-1">';
609 echo ' <div class="flex items-center space-x-3">';
610 echo ' <input type="email" id="' . $field_id . '_email" placeholder="' . esc_attr__('Enter email address to test', 'easy-invoice') . '" class="mt-1 block w-[200px] border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="' . esc_attr(get_option('admin_email')) . '">';
611 echo ' <button type="button" id="' . $field_id . '_button" class="test-email-button inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 h-[38px]">';
612 echo ' <i class="fas fa-paper-plane mr-2" aria-hidden="true"></i>';
613 echo ' ' . esc_html__('Send Test Email', 'easy-invoice');
614 echo ' </button>';
615 echo ' </div>';
616 echo ' <div id="' . $field_id . '_result" class="mt-2" style="display: none;"></div>';
617 echo '</div>';
618 break;
619 case 'test_template_email':
620 $template_type = $field_config['template_type'] ?? '';
621 $is_payment_test = ($field_id === 'easy_invoice_payment_test_template');
622 $width_class = $is_payment_test ? 'w-[200px]' : 'w-64';
623 echo '<div class="mt-1">';
624 echo ' <div class="flex items-center space-x-3">';
625 echo ' <input type="email" id="' . $field_id . '_email" placeholder="' . esc_attr__('Enter email address to test', 'easy-invoice') . '" class="mt-1 block ' . $width_class . ' border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="' . esc_attr(get_option('admin_email')) . '">';
626 echo ' <button type="button" id="' . $field_id . '_button" class="test-template-email-button inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 h-[38px]" data-template-type="' . esc_attr($template_type) . '">';
627 echo ' <i class="fas fa-envelope mr-2" aria-hidden="true"></i>';
628 echo ' ' . esc_html__('Test Template', 'easy-invoice');
629 echo ' </button>';
630 echo ' </div>';
631 echo ' <div id="' . $field_id . '_result" class="mt-2" style="display: none;"></div>';
632 echo '</div>';
633 break;
634 case 'test_payment_reminder_email':
635 echo '<div class="mt-1">';
636 echo ' <div class="flex items-center space-x-3">';
637 echo ' <input type="email" id="' . $field_id . '_email" placeholder="' . esc_attr__('Enter email address to test', 'easy-invoice') . '" class="mt-1 block w-64 border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="' . esc_attr(get_option('admin_email')) . '">';
638 echo ' <button type="button" id="' . $field_id . '_button" class="test-payment-reminder-email-button inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-orange-600 hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 h-[38px]">';
639 echo ' <i class="fas fa-bell mr-2" aria-hidden="true"></i>';
640 echo ' ' . esc_html__('Test Reminder', 'easy-invoice');
641 echo ' </button>';
642 echo ' </div>';
643 echo ' <div id="' . $field_id . '_result" class="mt-2" style="display: none;"></div>';
644 echo '</div>';
645 break;
646 default:
647 echo '<input type="text" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '" class="' . $input_class . '" placeholder="' . $field_placeholder . '">';
648 break;
649 }
650 if ($field_type !== 'checkbox' && $field_type !== 'wp_editor' && !empty($field_description)) {
651 echo '<p class="mt-1 text-sm text-gray-400">' . esc_html($field_description) . '</p>';
652 }
653 echo '</div>'; // Close the field wrapper div
654 }
655 }
656 ?>
657
658 <?php
659 // Show placeholders only for specific email subsections (not General)
660 if (in_array($subsection_key, ['invoice_available', 'quote_available', 'payment_received', 'payment_reminder'])): ?>
661 <div class="mt-6">
662 <div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
663 <p class="text-sm font-medium text-blue-900 mb-3"><?php esc_html_e('Available Placeholders:', 'easy-invoice'); ?></p>
664 <div class="grid grid-cols-2 md:grid-cols-3 gap-2">
665 <?php if (in_array($subsection_key, ['invoice_available', 'payment_received', 'payment_reminder'])): ?>
666 <!-- Invoice-specific placeholders -->
667 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{invoice_number}}</div>
668 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{due_date}}</div>
669 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{invoice_url}}</div>
670 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{payment_url}}</div>
671 <?php endif; ?>
672
673 <?php if (in_array($subsection_key, ['quote_available'])): ?>
674 <!-- Quote-specific placeholders -->
675 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{quote_number}}</div>
676 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{expiry_date}}</div>
677 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{quote_url}}</div>
678 <?php endif; ?>
679
680 <!-- Common placeholders for all templates -->
681 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{client_name}}</div>
682 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{company_name}}</div>
683 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{total_amount}}</div>
684 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{issue_date}}</div>
685 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{company_email}}</div>
686 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{company_phone}}</div>
687 <div class="bg-white border border-blue-200 rounded px-3 py-2 text-xs text-blue-700 font-mono">{{payment_terms}}</div>
688 <?php do_action('easy_invoice_settings_email_placeholders', $settings); ?>
689 </div>
690 </div>
691 </div>
692 <?php endif; ?>
693 </div>
694 </div>
695 <?php } ?>
696 <?php elseif ($section_id === 'payment' && !empty($all_gateways_sorted)): ?>
697 <div class="space-y-5">
698 <?php if (count($all_gateways_sorted) > 1): ?>
699 <div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
700 <div class="flex items-center">
701 <i class="fas fa-info-circle text-blue-500 mr-2"></i>
702 <p class="text-sm text-blue-700">
703 <?php _e('Drag and drop payment methods to reorder them. The order will be reflected on payment forms.', 'easy-invoice'); ?>
704 </p>
705 </div>
706 </div>
707 <?php endif; ?>
708
709 <div id="sortable-gateways" class="space-y-5">
710 <?php foreach ($all_gateways_sorted as $gateway_id => $gateway) : ?>
711 <div class="gateway-item bg-gray-50 p-5 rounded-lg border border-gray-200 cursor-move" data-gateway-id="<?php echo esc_attr($gateway_id); ?>">
712 <div class="flex items-center mb-3 space-x-3">
713 <!-- Drag Handle -->
714 <div class="gateway-handle text-gray-400 hover:text-gray-600 cursor-move mr-2">
715 <i class="fas fa-grip-vertical"></i>
716 </div>
717
718 <input type="checkbox"
719 class="gateway-enable-checkbox h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"
720 name="settings[easy_invoice_payment_methods][]"
721 id="gateway-enable-<?php echo esc_attr($gateway_id); ?>"
722 value="<?php echo esc_attr($gateway_id); ?>"
723 <?php checked(!empty($payment_methods_enabled) && in_array($gateway_id, $payment_methods_enabled)); ?>>
724
725 <div class="flex-1">
726 <label for="gateway-enable-<?php echo esc_attr($gateway_id); ?>" class="font-medium text-gray-900">
727 <?php echo esc_html(method_exists($gateway, 'getTitle') ? $gateway->getTitle() : $gateway_id); ?>
728 </label>
729 <?php if (method_exists($gateway, 'getDescription') && $gateway->getDescription()): ?>
730 <span class="block text-sm text-gray-400"><?php echo esc_html($gateway->getDescription()); ?></span>
731 <?php endif; ?>
732 </div>
733 </div>
734
735 <!-- Custom Display Name Field -->
736 <div class="gateway-settings <?php echo empty($payment_methods_enabled) || !in_array($gateway_id, $payment_methods_enabled) ? 'hidden' : ''; ?>">
737 <div class="mb-4">
738 <label for="gateway-display-name-<?php echo esc_attr($gateway_id); ?>" class="block text-sm font-medium text-gray-700">
739 <?php _e('Display Name', 'easy-invoice'); ?>
740 </label>
741 <input type="text"
742 id="gateway-display-name-<?php echo esc_attr($gateway_id); ?>"
743 name="settings[easy_invoice_gateway_display_name_<?php echo esc_attr($gateway_id); ?>]"
744 value="<?php echo esc_attr($settings['easy_invoice_gateway_display_name_' . $gateway_id] ?? (method_exists($gateway, 'getTitle') ? $gateway->getTitle() : ucfirst(str_replace('_', ' ', $gateway_id)))); ?>"
745 placeholder="<?php echo esc_attr(method_exists($gateway, 'getTitle') ? $gateway->getTitle() : ucfirst(str_replace('_', ' ', $gateway_id))); ?>"
746 class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
747 <p class="mt-1 text-xs text-gray-500">
748 <?php _e('Leave blank to use the default name. This name will appear on invoices and payment forms.', 'easy-invoice'); ?>
749 </p>
750 </div>
751
752 <?php
753 $gateway_config = method_exists($gateway, 'getSettingsConfig') ? $gateway->getSettingsConfig() : [];
754 if (!empty($gateway_config['fields'])): ?>
755 <div class="grid grid-cols-1 md:grid-cols-6 gap-4">
756 <?php foreach ($gateway_config['fields'] as $option_key => $field_config): ?>
757 <?php
758 $current_value = $settings[$option_key] ?? ($field_config['default'] ?? '');
759 easy_invoice_render_field($option_key, $field_config, $current_value);
760 ?>
761 <?php endforeach; ?>
762 </div>
763 <?php endif; ?>
764 </div>
765
766 <input type="hidden" class="gateway-order-input" name="settings[easy_invoice_gateway_order][]" value="<?php echo esc_attr($gateway_id); ?>">
767 </div>
768 <?php endforeach; ?>
769 </div>
770 </div>
771 <?php elseif (!empty($section_data['fields'])): ?>
772 <?php
773 // Check if this section has subsections
774 if (!empty($section_data['subsections'])) {
775 // Handle sections with subsections (like invoice, quote, etc.)
776 foreach ($section_data['subsections'] as $subsection_id => $subsection_data) {
777 $subsection_title = $subsection_data['title'] ?? '';
778 $subsection_description = $subsection_data['description'] ?? '';
779 ?>
780 <div class="subsection-content mb-8">
781 <div class="mb-4">
782 <h3 class="text-lg font-medium text-gray-900"><?php echo esc_html($subsection_title); ?></h3>
783 <?php if (!empty($subsection_description)): ?>
784 <p class="text-sm text-gray-400"><?php echo esc_html($subsection_description); ?></p>
785 <?php endif; ?>
786 </div>
787 <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
788 <?php if (!empty($subsection_data['fields'])) {
789 foreach ($subsection_data['fields'] as $option_key => $field_config) {
790 $current_value = $settings[$option_key] ?? ($field_config['default'] ?? '');
791 easy_invoice_render_field($option_key, $field_config, $current_value);
792 }
793 }
794 ?>
795 </div>
796 </div>
797 <?php
798 }
799 } else {
800 // Handle regular sections without subsections
801 if ($section_id === 'tax') {
802 // Render Enable Tax checkbox full width
803 $enable_tax_key = 'easy_invoice_tax_enabled';
804 $enable_tax_field = $section_data['fields'][$enable_tax_key];
805 $current_value = $settings[$enable_tax_key] ?? ($enable_tax_field['default'] ?? '');
806 echo '<div class="mb-4">';
807 echo '<div class="flex items-center">';
808 echo ' <input type="checkbox" name="settings[' . esc_attr($enable_tax_key) . ']" id="' . esc_attr($enable_tax_key) . '" value="yes" ' . checked($current_value, 'yes', false) . ' class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">';
809 echo ' <label for="' . esc_attr($enable_tax_key) . '" class="ml-2 block text-sm text-gray-900">' . esc_html($enable_tax_field['label']) . '</label>';
810 echo '</div>';
811 if (!empty($enable_tax_field['description'])) {
812 echo '<p id="' . $field_id . '-description" class="text-gray-400 mt-1 text-xs">' . esc_html($enable_tax_field['description']) . '</p>';
813 }
814 echo '</div>';
815 // Render the other three fields in a 3-column grid
816 echo '<div class="grid grid-cols-1 md:grid-cols-3 gap-6">';
817 $tax_keys = ['easy_invoice_tax_entry_method', 'easy_invoice_tax_rate', 'easy_invoice_tax_name'];
818 $select_class = 'mt-1 block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md';
819 $input_class = 'mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm';
820 foreach ($tax_keys as $tax_key) {
821 $field_config = $section_data['fields'][$tax_key];
822 $current_value = $settings[$tax_key] ?? ($field_config['default'] ?? '');
823 echo '<div>';
824 if ($field_config['type'] !== 'checkbox') {
825 echo '<label for="' . esc_attr($tax_key) . '" class="block text-sm font-medium text-gray-700">' . esc_html($field_config['label']) . '</label>';
826 }
827 switch ($field_config['type']) {
828 case 'text':
829 case 'email':
830 case 'url':
831 case 'tel':
832 echo '<input type="' . esc_attr($field_config['type']) . '" name="settings[' . esc_attr($tax_key) . ']" id="' . esc_attr($tax_key) . '" value="' . esc_attr($current_value) . '" class="' . $input_class . '" placeholder="' . esc_attr($field_config['placeholder'] ?? '') . '">';
833 break;
834 case 'number':
835 $min = isset($field_config['min']) ? ' min="' . esc_attr($field_config['min']) . '"' : '';
836 $max = isset($field_config['max']) ? ' max="' . esc_attr($field_config['max']) . '"' : '';
837 $step = isset($field_config['step']) ? ' step="' . esc_attr($field_config['step']) . '"' : '';
838 echo '<input type="number" name="settings[' . esc_attr($tax_key) . ']" id="' . esc_attr($tax_key) . '" value="' . esc_attr($current_value) . '"' . $min . $max . $step . ' class="' . $input_class . '" placeholder="' . esc_attr($field_config['placeholder'] ?? '') . '">';
839 break;
840 case 'select':
841 echo '<select id="' . esc_attr($tax_key) . '" name="settings[' . esc_attr($tax_key) . ']" class="' . $select_class . '">';
842 if (!empty($field_config['options']) && is_array($field_config['options'])) {
843 foreach ($field_config['options'] as $opt_val => $opt_label) {
844 echo '<option value="' . esc_attr($opt_val) . '" ' . selected($current_value, $opt_val, false) . '>' . esc_html($opt_label) . '</option>';
845 }
846 }
847 echo '</select>';
848 break;
849 }
850 if (!empty($field_config['description'])) {
851 echo '<p class="mt-1 text-xs text-gray-400">' . esc_html($field_config['description']) . '</p>';
852 }
853 echo '</div>';
854 }
855 echo '</div>';
856
857 // Allow additional fields to be added to the tax section
858 $additional_tax_fields = apply_filters('easy_invoice_tax_section_additional_fields', [], $settings);
859 if (!empty($additional_tax_fields)) {
860 echo '<div class="mt-6 pt-6 border-t border-gray-200">';
861 echo '<h4 class="text-sm font-medium text-gray-900 mb-4">' . __('Additional Tax Settings', 'easy-invoice-pro') . '</h4>';
862 echo '<div class="grid grid-cols-1 md:grid-cols-2 gap-6">';
863 foreach ($additional_tax_fields as $field_key => $field_config) {
864 $current_value = $settings[$field_key] ?? ($field_config['default'] ?? '');
865 easy_invoice_render_field($field_key, $field_config, $current_value);
866 }
867 echo '</div>';
868 echo '</div>';
869 }
870 } else {
871 // Use a two-column grid for currency and company settings
872 $is_grid = ($section_id === 'currency' || $section_id === 'company');
873 // Use a 6-column grid for invoice and quote settings to allow specific field pairing
874 $is_invoice_grid = ($section_id === 'invoice');
875 $is_quote_grid = ($section_id === 'quote');
876 // Use a specific grid for advanced settings to improve spacing
877 $is_advanced_grid = ($section_id === 'advanced');
878 // Use a 3-column grid for text settings
879 $is_text_settings_grid = ($section_id === 'text_settings');
880 if ($is_grid) echo '<div class="grid grid-cols-1 md:grid-cols-2 gap-6">';
881 if ($is_invoice_grid) echo '<div class="grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6">';
882 if ($is_quote_grid) echo '<div class="grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6">';
883 if ($is_advanced_grid) echo '<div class="grid grid-cols-1 gap-6">';
884 if ($is_text_settings_grid) echo '<div class="grid grid-cols-1 md:grid-cols-3 gap-6">';
885 foreach ($section_data['fields'] as $option_key => $field_config) {
886 $current_value = $settings[$option_key] ?? ($field_config['default'] ?? '');
887 $field_id = esc_attr($option_key);
888 $field_type = $field_config['type'] ?? 'text';
889 $field_label = esc_html($field_config['label'] ?? '');
890 $field_placeholder = isset($field_config['placeholder']) ? esc_attr($field_config['placeholder']) : '';
891 $field_description = $field_config['description'] ?? '';
892 $field_name = 'settings[' . esc_attr($option_key) . ']';
893 $input_class = 'mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm';
894 $select_class = 'mt-1 block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md';
895 $col_span_class = esc_attr($field_config['col_span'] ?? 'sm:col-span-6');
896 $form_group_class = '';
897 if ($is_grid) {
898 $form_group_class = '';
899 } elseif ($is_invoice_grid || $is_quote_grid || $is_advanced_grid || $is_text_settings_grid) {
900 $form_group_class = $col_span_class;
901 } else {
902 $form_group_class = $col_span_class;
903 }
904
905 // Handle conditional display
906 $conditional_class = '';
907 if (isset($field_config['depends_on']) && is_array($field_config['depends_on'])) {
908 $conditional_class = ' conditional-field';
909 foreach ($field_config['depends_on'] as $depends_key => $depends_value) {
910 $depends_field_name = 'settings[' . esc_attr($depends_key) . ']';
911 $conditional_class .= ' depends-on-' . esc_attr($depends_key) . '-' . esc_attr($depends_value);
912 }
913 }
914
915 echo '<div class="form-group ' . $form_group_class . $conditional_class . '">';
916 if ($field_type !== 'checkbox') {
917 echo '<label for="' . $field_id . '" class="block text-sm font-medium text-gray-700">' . $field_label . '</label>';
918 }
919
920 // Define required and aria_describedby variables
921 $required = !empty($field_config['required']) ? 'required' : '';
922 $aria_describedby = !empty($field_description) ? 'aria-describedby="' . $field_id . '-description"' : '';
923
924 switch ($field_type) {
925 case 'text':
926 case 'email':
927 case 'url':
928 case 'tel':
929 echo '<input type="' . esc_attr($field_type) . '" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '" class="' . $input_class . '" placeholder="' . $field_placeholder . '" ' . $required . ' ' . $aria_describedby . '>';
930 break;
931 case 'number':
932 $min = isset($field_config['min']) ? ' min="' . esc_attr($field_config['min']) . '"' : '';
933 $max = isset($field_config['max']) ? ' max="' . esc_attr($field_config['max']) . '"' : '';
934 $step = isset($field_config['step']) ? ' step="' . esc_attr($field_config['step']) . '"' : '';
935 echo '<input type="number" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '"' . $min . $max . $step . ' class="' . $input_class . '" placeholder="' . $field_placeholder . '" ' . $required . ' ' . $aria_describedby . '>';
936 break;
937 case 'textarea':
938 echo '<textarea id="' . $field_id . '" name="' . $field_name . '" rows="3" class="' . $input_class . '" placeholder="' . $field_placeholder . '" ' . $required . ' ' . $aria_describedby . '>' . wp_kses_post($current_value) . '</textarea>';
939 break;
940 case 'select':
941 echo '<select id="' . $field_id . '" name="' . $field_name . '" class="' . $select_class . '" ' . $required . ' ' . $aria_describedby . '>';
942 if (!empty($field_config['options']) && is_array($field_config['options'])) {
943 foreach ($field_config['options'] as $opt_val => $opt_label) {
944 echo '<option value="' . esc_attr($opt_val) . '" ' . selected(strtolower($current_value), strtolower($opt_val), false) . '>' . esc_html($opt_label) . '</option>';
945 }
946 }
947 echo '</select>';
948 break;
949 case 'multiselect':
950 echo '<select id="' . $field_id . '" name="' . $field_name . '[]" class="' . $select_class . '" multiple="multiple" aria-label="' . esc_attr($field_label) . '">';
951 if (!empty($field_config['options']) && is_array($field_config['options'])) {
952 foreach ($field_config['options'] as $opt_val => $opt_label) {
953 $selected = is_array($current_value) && in_array($opt_val, $current_value) ? 'selected="selected"' : '';
954 echo '<option value="' . esc_attr($opt_val) . '" ' . $selected . '>' . esc_html($opt_label) . '</option>';
955 }
956 }
957 echo '</select>';
958 break;
959 case 'readonly':
960 echo '<input type="text" id="' . $field_id . '" value="' . esc_attr($current_value) . '" class="' . $input_class . ' bg-gray-50" readonly>';
961 echo '<input type="hidden" name="' . $field_name . '" value="' . esc_attr($current_value) . '">';
962 break;
963 case 'checkbox':
964 echo '<div class="flex items-center">';
965 echo ' <input type="checkbox" name="' . $field_name . '" id="' . $field_id . '" value="yes" ' . checked($current_value, 'yes', false) . ' class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">';
966 echo ' <label for="' . $field_id . '" class="ml-2 block text-sm text-gray-900">' . $field_label . '</label>';
967 echo '</div>';
968 if (!empty($field_description)) {
969 echo '<p id="' . $field_id . '-description" class="mt-2 text-sm text-gray-400 leading-relaxed">' . esc_html($field_description) . '</p>';
970 }
971 break;
972 case 'image':
973 echo '<div class="mt-1 flex items-center">';
974 echo ' <span class="inline-block h-12 w-12 rounded-full overflow-hidden bg-gray-100">';
975 echo ' <img id="' . $field_id . '-preview" src="' . esc_url($current_value) . '" alt="' . esc_attr($field_label) . '" class="h-full w-full object-cover' . (empty($current_value) ? ' hidden' : '') . '">';
976 echo ' </span>';
977 echo ' <button type="button" id="upload_image_button_' . $field_id . '" class="upload-logo-button ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" data-uploader_title="' . esc_attr__('Choose Logo', 'easy-invoice') . '" data-uploader_button_text="' . esc_attr__('Select Logo', 'easy-invoice') . '">';
978 echo esc_html__('Change', 'easy-invoice');
979 echo '</button>';
980 echo ' <input type="hidden" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '">';
981 echo '</div>';
982 break;
983 case 'wp_editor':
984 wp_editor( $current_value, $field_id, ['textarea_name' => $field_name, 'teeny' => true, 'media_buttons' => false, 'textarea_rows' => 7, 'editor_class' => 'mt-1'] );
985 // Handle description for wp_editor specifically
986 if (!empty($field_description)) {
987 echo '<p class="mt-1 text-sm text-gray-400">' . esc_html($field_description) . '</p>';
988 }
989 break;
990 case 'color':
991 echo '<input type="color" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '" class="h-10 w-20 border border-gray-300 rounded">';
992 break;
993 case 'range':
994 $min = isset($field_config['min']) ? ' min="' . esc_attr($field_config['min']) . '"' : '';
995 $max = isset($field_config['max']) ? ' max="' . esc_attr($field_config['max']) . '"' : '';
996 $step = isset($field_config['step']) ? ' step="' . esc_attr($field_config['step']) . '"' : '';
997 echo '<input type="range" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '"' . $min . $max . $step . ' class="w-full">';
998 echo '<span class="text-sm text-gray-400">' . esc_html($current_value) . '%</span>';
999 break;
1000 case 'image_upload':
1001 echo '<div class="image-upload-wrap">';
1002 echo '<div class="flex items-center space-x-3">';
1003 echo ' <input type="hidden" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '">';
1004 echo ' <button type="button" class="upload-image-button bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" data-target="' . $field_id . '">';
1005 echo esc_html__('Upload Image', 'easy-invoice');
1006 echo '</button>';
1007 echo '</div>';
1008 echo '<div class="image-preview mt-2' . (empty($current_value) ? ' hidden' : '') . '">';
1009 if (!empty($current_value)) {
1010 echo '<img src="' . esc_url($current_value) . '" alt="Preview" style="max-width: 200px; max-height: 100px;">';
1011 }
1012 echo '</div>';
1013 echo '</div>';
1014 break;
1015 case 'button':
1016 $button_text = $field_config['button_text'] ?? $field_label;
1017 $button_class = $field_config['button_class'] ?? 'button button-secondary';
1018 $button_class_name = 'regenerate-invoice-numbers-button';
1019 if ($option_key === 'easy_invoice_regenerate_quote_numbers') {
1020 $button_class_name = 'regenerate-quote-numbers-button';
1021 }
1022 echo '<button type="button" id="' . $field_id . '" class="' . esc_attr($button_class) . ' ' . $button_class_name . '">';
1023 echo esc_html($button_text);
1024 echo '</button>';
1025 break;
1026 default:
1027 echo '<input type="text" name="' . $field_name . '" id="' . $field_id . '" value="' . esc_attr($current_value) . '" class="' . $input_class . '" placeholder="' . $field_placeholder . '">';
1028 break;
1029 }
1030 if ($field_type !== 'checkbox' && $field_type !== 'wp_editor' && !empty($field_description)) {
1031 echo '<p class="mt-1 text-sm text-gray-400">' . esc_html($field_description) . '</p>';
1032 }
1033 echo '</div>';
1034 }
1035 if ($is_grid) echo '</div>';
1036 if ($is_invoice_grid) echo '</div>';
1037 if ($is_quote_grid) echo '</div>';
1038 if ($is_advanced_grid) echo '</div>';
1039 if ($is_text_settings_grid) echo '</div>';
1040 }
1041 }
1042 ?>
1043 <?php endif; ?>
1044 </div>
1045 </div>
1046 </div>
1047 <?php endforeach; ?>
1048
1049 <!-- Bottom Save Button -->
1050 <div class="mt-8 pt-6 border-t border-gray-200">
1051 <div class="flex items-center justify-start space-x-3">
1052 <button type="button" id="save-settings-bottom" 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 transition-colors duration-200" aria-label="<?php esc_attr_e('Save Settings', 'easy-invoice'); ?>">
1053 <i class="fas fa-save mr-2" aria-hidden="true"></i>
1054 <?php esc_html_e('Save Changes', 'easy-invoice'); ?>
1055 </button>
1056 </div>
1057 </div>
1058
1059 </form>
1060 </div>
1061 </div>
1062 </div>
1063 </div>
1064
1065 <script>
1066 document.addEventListener('DOMContentLoaded', function() {
1067 // Enter key functionality to save settings
1068 const settingsForm = document.getElementById('settings-form');
1069 const saveButton = document.getElementById('save-settings-bottom');
1070
1071 if (settingsForm && saveButton) {
1072 // Get both save buttons
1073 const topSaveButton = document.getElementById('save-settings');
1074 const bottomSaveButton = document.getElementById('save-settings-bottom');
1075
1076 // Function to trigger save
1077 function triggerSave() {
1078 // Try bottom button first, then top button
1079 if (bottomSaveButton) {
1080 bottomSaveButton.click();
1081 } else if (topSaveButton) {
1082 topSaveButton.click();
1083 }
1084 }
1085
1086 // Listen for Enter key on form inputs
1087 settingsForm.addEventListener('keydown', function(e) {
1088 // Check if Enter key is pressed and not in a textarea or select
1089 if (e.key === 'Enter' && e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'SELECT') {
1090 e.preventDefault();
1091 triggerSave();
1092 }
1093 });
1094
1095 // Also listen for Enter key on the entire form
1096 settingsForm.addEventListener('keypress', function(e) {
1097 if (e.key === 'Enter' && e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'SELECT') {
1098 e.preventDefault();
1099 triggerSave();
1100 }
1101 });
1102 }
1103
1104 // Email Settings submenu hover behavior for settings page
1105 const emailNavLink = document.querySelector('.nav-link[href*="section=email"]');
1106 const emailSubmenu = document.querySelector('.email-settings-submenu');
1107
1108 if (emailNavLink && emailSubmenu) {
1109 // Check if any Email Settings subsection is active
1110 const activeSubmenuLink = document.querySelector('.email-submenu-link.text-indigo-700');
1111 const isEmailSectionActive = emailNavLink.classList.contains('text-indigo-700');
1112 const isAnySubsectionActive = activeSubmenuLink !== null;
1113
1114 // If any subsection is active, show submenu permanently and disable hover effects
1115 if (isAnySubsectionActive || isEmailSectionActive) {
1116 emailSubmenu.style.display = 'block';
1117 emailSubmenu.style.opacity = '1';
1118 emailSubmenu.style.visibility = 'visible';
1119 emailSubmenu.style.maxHeight = '200px';
1120 emailSubmenu.classList.add('show');
1121 return; // Exit early, no hover effects needed
1122 }
1123
1124 // Only add hover effects if no subsection is active
1125 // Show submenu on hover
1126 emailNavLink.addEventListener('mouseenter', function() {
1127 emailSubmenu.style.display = 'block';
1128 emailSubmenu.classList.add('show');
1129 setTimeout(() => {
1130 emailSubmenu.style.opacity = '1';
1131 emailSubmenu.style.visibility = 'visible';
1132 emailSubmenu.style.maxHeight = '200px';
1133 }, 10);
1134 });
1135
1136 // Hide submenu when mouse leaves
1137 emailNavLink.addEventListener('mouseleave', function() {
1138 // Only hide if not hovering over submenu
1139 setTimeout(() => {
1140 if (!emailSubmenu.matches(':hover')) {
1141 emailSubmenu.style.opacity = '0';
1142 emailSubmenu.style.visibility = 'hidden';
1143 emailSubmenu.style.maxHeight = '0';
1144 emailSubmenu.classList.remove('show');
1145 setTimeout(() => {
1146 emailSubmenu.style.display = 'none';
1147 }, 200);
1148 }
1149 }, 100);
1150 });
1151
1152 // Keep submenu open when hovering over submenu items
1153 emailSubmenu.addEventListener('mouseenter', function() {
1154 emailSubmenu.style.opacity = '1';
1155 emailSubmenu.style.visibility = 'visible';
1156 emailSubmenu.style.maxHeight = '200px';
1157 emailSubmenu.classList.add('show');
1158 });
1159
1160 emailSubmenu.addEventListener('mouseleave', function() {
1161 emailSubmenu.style.opacity = '0';
1162 emailSubmenu.style.visibility = 'hidden';
1163 emailSubmenu.style.maxHeight = '0';
1164 emailSubmenu.classList.remove('show');
1165 setTimeout(() => {
1166 emailSubmenu.style.display = 'none';
1167 }, 200);
1168 });
1169 }
1170 });
1171 </script>
1172
1173 <style>
1174 #screen-meta-links{
1175 display:none;
1176 }
1177 /* Email Settings Submenu Styles for Settings Page */
1178 .email-settings-submenu {
1179 transition: all 0.3s ease;
1180 overflow: hidden;
1181 }
1182
1183 .email-settings-submenu.show {
1184 display: block !important;
1185 }
1186
1187 .email-settings-submenu.show ~ .nav-link .fas.fa-chevron-down,
1188 .email-settings-submenu:hover ~ .nav-link .fas.fa-chevron-down {
1189 transform: rotate(180deg);
1190 }
1191
1192 /* Custom color for settings navigation icons */
1193 .nav-link:hover svg {
1194 color: rgb(79 70 229) !important;
1195 }
1196
1197 /* Active state icon color */
1198 .bg-indigo-50 svg {
1199 color: rgb(79 70 229) !important;
1200 }
1201
1202 /* Hover state for non-active items */
1203 .nav-link:hover:not(.bg-indigo-50) svg {
1204 color: rgb(79 70 229) !important;
1205 }
1206
1207 /* Email submenu icon colors */
1208 .email-submenu-link:hover svg {
1209 color: rgb(79 70 229) !important;
1210 }
1211
1212 .email-submenu-link.bg-indigo-50 svg {
1213 color: rgb(79 70 229) !important;
1214 }
1215 </style>