ultimate-jekyll-manager 0.0.119 → 0.0.121

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/CLAUDE.md +102 -2
  2. package/README.md +171 -2
  3. package/TODO.md +10 -2
  4. package/_backup/form-manager.backup.js +1020 -0
  5. package/dist/assets/js/libs/auth/pages.js +64 -136
  6. package/dist/assets/js/libs/form-manager.js +643 -775
  7. package/dist/assets/js/pages/account/sections/api-keys.js +37 -52
  8. package/dist/assets/js/pages/account/sections/connections.js +37 -46
  9. package/dist/assets/js/pages/account/sections/delete.js +46 -66
  10. package/dist/assets/js/pages/account/sections/profile.js +37 -56
  11. package/dist/assets/js/pages/account/sections/security.js +100 -126
  12. package/dist/assets/js/pages/admin/notifications/new/index.js +72 -157
  13. package/dist/assets/js/pages/blog/index.js +29 -51
  14. package/dist/assets/js/pages/contact/index.js +110 -144
  15. package/dist/assets/js/pages/download/index.js +38 -86
  16. package/dist/assets/js/pages/oauth2/index.js +17 -17
  17. package/dist/assets/js/pages/payment/checkout/index.js +23 -36
  18. package/dist/assets/js/pages/test/libraries/form-manager/index.js +194 -0
  19. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signin.html +2 -2
  20. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signup.html +2 -2
  21. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html +10 -37
  22. package/dist/defaults/dist/pages/test/libraries/form-manager.html +181 -0
  23. package/dist/gulp/tasks/serve.js +18 -0
  24. package/dist/lib/logger.js +1 -1
  25. package/firebase-debug.log +420 -0
  26. package/package.json +11 -7
  27. package/.playwright-mcp/page-2025-10-22T19-11-27-666Z.png +0 -0
  28. package/.playwright-mcp/page-2025-10-22T19-11-57-357Z.png +0 -0
@@ -123,47 +123,31 @@ function trackAddPaymentInfo(product, price, billingCycle, paymentMethod) {
123
123
  function setupEventListeners() {
124
124
  // Initialize FormManager
125
125
  formManager = new FormManager('#checkout-form', {
126
- autoDisable: true, // Enable automatic form disabling during submission
127
- showSpinner: true,
128
- allowMultipleSubmissions: false, // Prevent multiple submissions
129
- submitButtonLoadingText: 'Processing...'
126
+ autoReady: false, // We'll call ready() after initialization
127
+ allowResubmit: false,
128
+ submittingText: 'Processing...',
129
+ submittedText: 'Redirecting...',
130
130
  });
131
131
 
132
132
  // Listen for form field changes
133
- formManager.addEventListener('change', (event) => {
134
- const { fieldName, fieldValue, data } = event.detail;
135
-
133
+ formManager.on('change', ({ name, value }) => {
136
134
  // Handle billing cycle changes
137
- if (fieldName === 'billing-cycle') {
138
- handleBillingCycleChange(fieldValue, webManager);
139
- }
140
- });
141
-
142
- // Handle non-submit button clicks
143
- formManager.addEventListener('button', (event) => {
144
- const { action } = event.detail;
145
-
146
- if (action === 'apply-discount') {
147
- applyDiscountCode(webManager);
135
+ if (name === 'billing-cycle') {
136
+ handleBillingCycleChange(value, webManager);
148
137
  }
149
138
  });
150
139
 
151
140
  // Handle form submission
152
- formManager.addEventListener('submit', async (event) => {
153
- event.preventDefault();
154
-
141
+ formManager.on('submit', async ({ $submitButton }) => {
155
142
  // Get the submit button that was clicked
156
- const submitButton = event.detail.submitButton;
157
- if (!submitButton) {
158
- formManager.showError('Please choose a payment method.');
159
- return;
143
+ if (!$submitButton) {
144
+ throw new Error('Please choose a payment method.');
160
145
  }
161
146
 
162
147
  // Check if a payment method was selected
163
- const paymentMethod = submitButton.getAttribute('data-payment-method');
148
+ const paymentMethod = $submitButton.getAttribute('data-payment-method');
164
149
  if (!paymentMethod) {
165
- formManager.showError('Invalid payment method selected.');
166
- return;
150
+ throw new Error('Invalid payment method selected.');
167
151
  }
168
152
 
169
153
  // Set payment method in raw
@@ -181,6 +165,14 @@ function setupEventListeners() {
181
165
  await completePurchase();
182
166
  });
183
167
 
168
+ // Setup apply discount button (not part of form submit)
169
+ const $applyDiscountBtn = document.querySelector('[data-action="apply-discount"]');
170
+ if ($applyDiscountBtn) {
171
+ $applyDiscountBtn.addEventListener('click', () => {
172
+ applyDiscountCode(webManager);
173
+ });
174
+ }
175
+
184
176
  // Switch account link (keep as is - not part of form)
185
177
  const $switchAccountLink = document.getElementById('switch-account');
186
178
  if ($switchAccountLink) {
@@ -274,13 +266,8 @@ async function completePurchase() {
274
266
  } catch (error) {
275
267
  console.error('Purchase error:', error);
276
268
 
277
- // FormManager will handle button state restoration
278
- formManager.setFormState('ready');
279
-
280
- // Show user-friendly error message
281
- const errorMessage = error.message || 'There was an error processing your payment. Please try again.';
282
-
283
- formManager.showError(errorMessage);
269
+ // Re-throw to let FormManager handle error display and state restoration
270
+ throw error;
284
271
  }
285
272
  }
286
273
 
@@ -385,7 +372,7 @@ async function initializeCheckout() {
385
372
  setupEventListeners();
386
373
 
387
374
  // Set form to ready state
388
- formManager.setFormState('ready');
375
+ formManager.ready();
389
376
 
390
377
  // Track begin_checkout event on page load
391
378
  const basePrice = raw.product.is_subscription
@@ -0,0 +1,194 @@
1
+ /**
2
+ * FormManager Test Page JavaScript
3
+ */
4
+
5
+ // Libraries
6
+ import { FormManager } from '__main_assets__/js/libs/form-manager.js';
7
+
8
+ let webManager = null;
9
+
10
+ // Module
11
+ export default (Manager) => {
12
+ return new Promise(async function (resolve) {
13
+ // Shortcuts
14
+ webManager = Manager.webManager;
15
+
16
+ // Initialize when DOM is ready
17
+ await webManager.dom().ready();
18
+
19
+ // Initialize test forms
20
+ initTestFormMain();
21
+ initTestFormValidation();
22
+ initTestFormContact();
23
+ initTestFormManual();
24
+
25
+ // Resolve after initialization
26
+ return resolve();
27
+ });
28
+ };
29
+
30
+ // Helper: simulate async API call
31
+ function simulateApi(ms = 1000) {
32
+ return new Promise(resolve => setTimeout(resolve, ms));
33
+ }
34
+
35
+ // Test 1: Full Test (success/fail, nested, change events)
36
+ function initTestFormMain() {
37
+ const formManager = new FormManager('#test-form-main');
38
+ const $status = document.getElementById('main-status');
39
+ const $action = document.getElementById('main-action');
40
+ const $output = document.getElementById('main-output');
41
+
42
+ formManager.on('statechange', ({ state }) => {
43
+ $status.textContent = `Status: ${state}`;
44
+ });
45
+
46
+ formManager.on('change', ({ name, value, data }) => {
47
+ console.log('[Test 1] Change:', name, '=', value);
48
+ console.log('[Test 1] Full data:', data);
49
+ });
50
+
51
+ formManager.on('submit', async ({ data, $submitButton }) => {
52
+ console.log('[Test 1] Submitting:', data);
53
+ console.log('[Test 1] Submit button:', $submitButton?.dataset?.action);
54
+
55
+ // Show action separately
56
+ const action = $submitButton?.dataset?.action || 'unknown';
57
+ $action.textContent = `Action: ${action}`;
58
+
59
+ // Show data
60
+ $output.textContent = JSON.stringify(data, null, 2);
61
+
62
+ await simulateApi(1000);
63
+
64
+ if (data.settings.outcome === 'error') {
65
+ throw new Error('Simulated server error - please try again');
66
+ }
67
+
68
+ formManager.showSuccess(`Form ${action === 'draft' ? 'saved as draft' : 'submitted'} successfully!`);
69
+ });
70
+
71
+ // Set Data button - test setData() API
72
+ const $setDataBtn = document.getElementById('main-set-data');
73
+ $setDataBtn.addEventListener('click', () => {
74
+ const testData = {
75
+ user: {
76
+ name: 'John Doe',
77
+ email: 'john@example.com',
78
+ address: {
79
+ city: 'Los Angeles',
80
+ },
81
+ },
82
+ settings: {
83
+ outcome: 'error',
84
+ subscribe: true,
85
+ },
86
+ preferences: {
87
+ notifications: 'important',
88
+ features: {
89
+ darkmode: true,
90
+ analytics: true,
91
+ beta: false,
92
+ },
93
+ },
94
+ };
95
+
96
+ formManager.setData(testData);
97
+ $output.textContent = 'Data set via setData() API - submit to verify';
98
+ });
99
+ }
100
+
101
+ // Test 2: Validation
102
+ function initTestFormValidation() {
103
+ const formManager = new FormManager('#test-form-validation');
104
+ const $status = document.getElementById('validation-status');
105
+ const $setCorrectBtn = document.getElementById('validation-set-correct');
106
+
107
+ formManager.on('statechange', ({ state }) => {
108
+ $status.textContent = `Status: ${state}`;
109
+ });
110
+
111
+ // Validation event - runs BEFORE submit, use setError to accumulate errors
112
+ formManager.on('validation', ({ data, setError }) => {
113
+ console.log('[Test 2] Validating:', data);
114
+
115
+ // Custom validation (HTML5 validation handles required, email format, etc.)
116
+ // Here we add business logic validation
117
+
118
+ if (data.age && parseInt(data.age) < 18) {
119
+ setError('age', 'You must be 18 or older');
120
+ }
121
+
122
+ // Note: 'required' validation is handled automatically by HTML5 validation
123
+ // We just need to add the 'required' attribute to the HTML inputs
124
+ });
125
+
126
+ formManager.on('submit', async ({ data }) => {
127
+ console.log('[Test 2] Submitting (validation passed):', data);
128
+
129
+ // If we reach here, validation passed
130
+ await simulateApi(500);
131
+ formManager.showSuccess('Validation passed! Form submitted.');
132
+ });
133
+
134
+ // Set Correct button - fills in valid values
135
+ $setCorrectBtn.addEventListener('click', () => {
136
+ formManager.setData({
137
+ name: 'John Doe',
138
+ email: 'john@example.com',
139
+ age: 25,
140
+ terms: true,
141
+ });
142
+ });
143
+ }
144
+
145
+ // Test 3: Contact Form (one-time submit)
146
+ function initTestFormContact() {
147
+ const formManager = new FormManager('#test-form-contact', {
148
+ allowResubmit: false,
149
+ resetOnSuccess: true,
150
+ });
151
+ const $status = document.getElementById('contact-status');
152
+
153
+ formManager.on('statechange', ({ state }) => {
154
+ $status.textContent = `Status: ${state}`;
155
+ });
156
+
157
+ formManager.on('change', ({ name, value, data }) => {
158
+ console.log('[Test 3] Change:', name, '=', value);
159
+ console.log('[Test 3] Full data:', data);
160
+ });
161
+
162
+ formManager.on('submit', async ({ data }) => {
163
+ console.log('[Test 3] Submitting:', data);
164
+ await simulateApi(1000);
165
+ formManager.showSuccess('Message sent! Form is now locked.');
166
+ });
167
+ }
168
+
169
+ // Test 4: Manual Ready
170
+ function initTestFormManual() {
171
+ const formManager = new FormManager('#test-form-manual', { autoReady: false });
172
+ const $status = document.getElementById('manual-status');
173
+
174
+ formManager.on('statechange', ({ state }) => {
175
+ $status.textContent = `Status: ${state}`;
176
+ });
177
+
178
+ formManager.on('change', ({ name, value, data }) => {
179
+ console.log('[Test 4] Change:', name, '=', value);
180
+ console.log('[Test 4] Full data:', data);
181
+ });
182
+
183
+ formManager.on('submit', async ({ data }) => {
184
+ console.log('[Test 4] Submitting:', data);
185
+ await simulateApi(1000);
186
+ formManager.showSuccess('Done!');
187
+ });
188
+
189
+ // Simulate async initialization (e.g., loading user data)
190
+ setTimeout(() => {
191
+ console.log('[Test 4] Now ready');
192
+ formManager.ready();
193
+ }, 2000);
194
+ }
@@ -63,7 +63,7 @@ social_signin:
63
63
  <label for="email" class="form-label fw-semibold">
64
64
  Email <span class="text-danger">*</span>
65
65
  </label>
66
- <input type="email" class="form-control form-control-md" id="email" name="email" placeholder="Enter your email" autocomplete="email" autofocus required>
66
+ <input type="email" class="form-control form-control-md" id="email" name="email" placeholder="Enter your email" autocomplete="email" autofocus>
67
67
  </div>
68
68
 
69
69
  <div class="mb-3 text-start">
@@ -71,7 +71,7 @@ social_signin:
71
71
  Password <span class="text-danger">*</span>
72
72
  </label>
73
73
  <div class="input-group">
74
- <input type="password" class="form-control form-control-md" id="password" name="password" placeholder="Enter your password" autocomplete="current-password" required>
74
+ <input type="password" class="form-control form-control-md" id="password" name="password" placeholder="Enter your password" autocomplete="current-password">
75
75
  <button class="btn border uj-password-toggle px-3" type="button" aria-label="Toggle password visibility">
76
76
  <span class="uj-password-show">{% uj_icon "eye", "fa-fw" %}</span>
77
77
  <span class="uj-password-hide d-none">{% uj_icon "eye-slash", "fa-fw" %}</span>
@@ -73,7 +73,7 @@ social_signup:
73
73
  <label for="email" class="form-label fw-semibold">
74
74
  Email <span class="text-danger">*</span>
75
75
  </label>
76
- <input type="email" class="form-control form-control-md" id="email" name="email" placeholder="Enter your email" autocomplete="email" autofocus required>
76
+ <input type="email" class="form-control form-control-md" id="email" name="email" placeholder="Enter your email" autocomplete="email" autofocus>
77
77
  </div>
78
78
 
79
79
  <div class="mb-3 text-start">
@@ -81,7 +81,7 @@ social_signup:
81
81
  Password <span class="text-danger">*</span>
82
82
  </label>
83
83
  <div class="input-group">
84
- <input type="password" class="form-control form-control-md" id="password" name="password" placeholder="Create a strong password" autocomplete="new-password" required>
84
+ <input type="password" class="form-control form-control-md" id="password" name="password" placeholder="Create a strong password" autocomplete="new-password">
85
85
  <button class="btn border uj-password-toggle px-3" type="button" aria-label="Toggle password visibility">
86
86
  <span class="uj-password-show">{% uj_icon "eye", "fa-fw" %}</span>
87
87
  <span class="uj-password-hide d-none">{% uj_icon "eye-slash", "fa-fw" %}</span>
@@ -178,37 +178,24 @@ faqs:
178
178
  {% endiftruthy %}
179
179
  </div>
180
180
 
181
- <!-- Error Container -->
182
- <div class="alert alert-danger contact-error-alert mb-3 d-none" role="alert"></div>
183
-
184
- <!-- Success Container -->
185
- <div class="alert alert-success contact-success-alert mb-3 d-none" role="alert"></div>
186
-
187
- <form id="contact-form" autocomplete="on" novalidate>
181
+ <form id="contact-form" autocomplete="on">
188
182
  <div class="row g-3 mb-4">
189
183
  <div class="col-md-6">
190
- <label for="first_name" class="form-label fw-semibold">
191
- First Name <span class="text-danger">*</span>
192
- </label>
184
+ <label for="first_name" class="form-label fw-semibold">First Name <span class="text-danger">*</span></label>
193
185
  <input type="text" class="form-control form-control-lg" id="first_name" name="first_name"
194
186
  placeholder="Enter your first name"
195
187
  autocomplete="given-name"
196
- data-invalid-field="first_name"
197
- data-invalid-description="Please enter your first name"
198
188
  minlength="2"
199
189
  maxlength="50"
190
+ autofocus
200
191
  required>
201
192
  <div class="invalid-feedback"></div>
202
193
  </div>
203
194
  <div class="col-md-6">
204
- <label for="last_name" class="form-label fw-semibold">
205
- Last Name <span class="text-danger">*</span>
206
- </label>
195
+ <label for="last_name" class="form-label fw-semibold">Last Name <span class="text-danger">*</span></label>
207
196
  <input type="text" class="form-control form-control-lg" id="last_name" name="last_name"
208
197
  placeholder="Enter your last name"
209
198
  autocomplete="family-name"
210
- data-invalid-field="last_name"
211
- data-invalid-description="Please enter your last name"
212
199
  minlength="2"
213
200
  maxlength="50"
214
201
  required>
@@ -222,14 +209,10 @@ faqs:
222
209
  </div>
223
210
 
224
211
  <div class="mb-4">
225
- <label for="email" class="form-label fw-semibold">
226
- Email Address <span class="text-danger">*</span>
227
- </label>
212
+ <label for="email" class="form-label fw-semibold">Email Address <span class="text-danger">*</span></label>
228
213
  <input type="email" class="form-control form-control-lg" id="email" name="email"
229
214
  placeholder="Enter your email address"
230
215
  autocomplete="email"
231
- data-invalid-field="email"
232
- data-invalid-description="Please enter a valid email address"
233
216
  required>
234
217
  <div class="invalid-feedback"></div>
235
218
  </div>
@@ -240,17 +223,11 @@ faqs:
240
223
  placeholder="Enter your company name (optional)"
241
224
  autocomplete="organization"
242
225
  maxlength="100">
243
- <div class="invalid-feedback"></div>
244
226
  </div>
245
227
 
246
228
  <div class="mb-4">
247
- <label for="subject" class="form-label fw-semibold">
248
- Subject <span class="text-danger">*</span>
249
- </label>
250
- <select class="form-select form-select-lg" id="subject" name="subject"
251
- data-invalid-field="subject"
252
- data-invalid-description="Please select a subject"
253
- required>
229
+ <label for="subject" class="form-label fw-semibold">Subject <span class="text-danger">*</span></label>
230
+ <select class="form-select form-select-lg" id="subject" name="subject" required>
254
231
  <option value="">Choose a topic...</option>
255
232
  <option value="general">General Inquiry</option>
256
233
  <option value="support">Technical Support</option>
@@ -267,13 +244,9 @@ faqs:
267
244
  </div>
268
245
 
269
246
  <div class="mb-4">
270
- <label for="message" class="form-label fw-semibold">
271
- Message <span class="text-danger">*</span>
272
- </label>
247
+ <label for="message" class="form-label fw-semibold">Message <span class="text-danger">*</span></label>
273
248
  <textarea class="form-control form-control-lg" id="message" name="message" rows="3"
274
249
  placeholder="Tell us how we can help you..."
275
- data-invalid-field="message"
276
- data-invalid-description="Please enter your message"
277
250
  minlength="10"
278
251
  maxlength="5000"
279
252
  required></textarea>
@@ -281,9 +254,9 @@ faqs:
281
254
  </div>
282
255
 
283
256
  <div class="d-grid gap-2 d-md-flex justify-content-md-between align-items-center">
284
- <button type="submit" class="btn btn-primary btn-lg px-5" disabled>
257
+ <button type="submit" class="btn btn-primary btn-lg px-5">
285
258
  {% uj_icon "paper-plane", "me-2" %}
286
- <span class="button-text">Send Message</span>
259
+ Send Message
287
260
  </button>
288
261
  <a href="{{ site.url }}" class="btn btn-link">
289
262
  {% uj_icon "arrow-left", "me-1" %}
@@ -0,0 +1,181 @@
1
+ ---
2
+ layout: themes/[ site.theme.id ]/frontend/core/base
3
+ title: FormManager Test
4
+ permalink: /test/libraries/form-manager
5
+ sitemap: false
6
+ ---
7
+
8
+ <div class="container py-5">
9
+ <h1 class="mb-4">FormManager Test Page</h1>
10
+ <p class="text-muted mb-5">Testing different configurations of the FormManager library.</p>
11
+
12
+ <div class="row g-4">
13
+
14
+ <!-- Test 1: Full Test (success/fail, nested, change events) -->
15
+ <div class="col-lg-8">
16
+ <div class="card">
17
+ <div class="card-header">
18
+ <h5 class="mb-0">Test 1: Full Test</h5>
19
+ <small class="text-muted">Success/failure toggle, nested dot notation, change events (check console)</small>
20
+ </div>
21
+ <div class="card-body">
22
+ <form id="test-form-main">
23
+ <div class="row">
24
+ <div class="col-md-6">
25
+ <div class="mb-3">
26
+ <label for="main-name" class="form-label">user.name</label>
27
+ <input type="text" class="form-control" id="main-name" name="user.name" value="Ian">
28
+ </div>
29
+ <div class="mb-3">
30
+ <label for="main-email" class="form-label">user.email</label>
31
+ <input type="email" class="form-control" id="main-email" name="user.email" value="ian@example.com">
32
+ </div>
33
+ <div class="mb-3">
34
+ <label for="main-city" class="form-label">user.address.city</label>
35
+ <input type="text" class="form-control" id="main-city" name="user.address.city" value="NYC">
36
+ </div>
37
+ </div>
38
+ <div class="col-md-6">
39
+ <div class="mb-3">
40
+ <label for="main-outcome" class="form-label">Simulate Outcome</label>
41
+ <select class="form-select" id="main-outcome" name="settings.outcome">
42
+ <option value="success">Success</option>
43
+ <option value="error">Error (throw Error)</option>
44
+ </select>
45
+ </div>
46
+ <div class="mb-3">
47
+ <label class="form-label d-block">preferences.notifications (radio group)</label>
48
+ <div class="form-check form-check-inline">
49
+ <input type="radio" class="form-check-input" id="notif-all" name="preferences.notifications" value="all" checked>
50
+ <label class="form-check-label" for="notif-all">All</label>
51
+ </div>
52
+ <div class="form-check form-check-inline">
53
+ <input type="radio" class="form-check-input" id="notif-important" name="preferences.notifications" value="important">
54
+ <label class="form-check-label" for="notif-important">Important</label>
55
+ </div>
56
+ <div class="form-check form-check-inline">
57
+ <input type="radio" class="form-check-input" id="notif-none" name="preferences.notifications" value="none">
58
+ <label class="form-check-label" for="notif-none">None</label>
59
+ </div>
60
+ </div>
61
+ <div class="mb-3">
62
+ <label class="form-label d-block">preferences.features (checkbox group)</label>
63
+ <div class="form-check">
64
+ <input type="checkbox" class="form-check-input" id="feat-darkmode" name="preferences.features" value="darkmode">
65
+ <label class="form-check-label" for="feat-darkmode">Dark Mode</label>
66
+ </div>
67
+ <div class="form-check">
68
+ <input type="checkbox" class="form-check-input" id="feat-analytics" name="preferences.features" value="analytics">
69
+ <label class="form-check-label" for="feat-analytics">Analytics</label>
70
+ </div>
71
+ <div class="form-check">
72
+ <input type="checkbox" class="form-check-input" id="feat-beta" name="preferences.features" value="beta">
73
+ <label class="form-check-label" for="feat-beta">Beta Features</label>
74
+ </div>
75
+ </div>
76
+ <div class="mb-3 form-check">
77
+ <input type="checkbox" class="form-check-input" id="main-subscribe" name="settings.subscribe">
78
+ <label class="form-check-label" for="main-subscribe">settings.subscribe (single checkbox)</label>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ <div class="d-flex gap-2">
83
+ <button type="submit" data-action="save" class="btn btn-primary">Save</button>
84
+ <button type="submit" data-action="draft" class="btn btn-outline-secondary">Save as Draft</button>
85
+ <button type="button" id="main-set-data" class="btn btn-outline-info">Set Data</button>
86
+ </div>
87
+ </form>
88
+ <div class="mt-3 d-flex gap-3">
89
+ <small class="text-muted" id="main-status">Status: ready</small>
90
+ <small class="text-muted" id="main-action">Action: -</small>
91
+ </div>
92
+ <div class="mt-3">
93
+ <pre id="main-output" class="bg-body-secondary p-2 rounded small" style="max-height: 200px; overflow: auto;">Submit to see collected data...</pre>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+
99
+ <!-- Test 2: Validation -->
100
+ <div class="col-lg-4">
101
+ <div class="card">
102
+ <div class="card-header d-flex justify-content-between align-items-start">
103
+ <div>
104
+ <h5 class="mb-0">Test 2: Validation</h5>
105
+ <small class="text-muted">HTML5 + custom validation event</small>
106
+ </div>
107
+ <button type="button" id="validation-set-correct" class="btn btn-sm btn-outline-success">Set Correct</button>
108
+ </div>
109
+ <div class="card-body">
110
+ <form id="test-form-validation">
111
+ <div class="mb-3">
112
+ <label for="validation-name" class="form-label">Name (required)</label>
113
+ <input type="text" class="form-control" id="validation-name" name="name" required>
114
+ </div>
115
+ <div class="mb-3">
116
+ <label for="validation-email" class="form-label">Email (required, valid format)</label>
117
+ <input type="email" class="form-control" id="validation-email" name="email" required>
118
+ </div>
119
+ <div class="mb-3">
120
+ <label for="validation-age" class="form-label">Age (required, must be 18+)</label>
121
+ <input type="number" class="form-control" id="validation-age" name="age" required min="1">
122
+ </div>
123
+ <div class="mb-3 form-check">
124
+ <input type="checkbox" class="form-check-input" id="validation-terms" name="terms" required>
125
+ <label class="form-check-label" for="validation-terms">I agree to the terms (required)</label>
126
+ </div>
127
+ <button type="submit" class="btn btn-primary">Submit</button>
128
+ </form>
129
+ <div class="mt-2">
130
+ <small class="text-muted" id="validation-status">Status: ready</small>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </div>
135
+
136
+ <!-- Test 3: Contact Form (one-time submit, reset on success) -->
137
+ <div class="col-lg-4">
138
+ <div class="card">
139
+ <div class="card-header">
140
+ <h5 class="mb-0">Test 3: Contact Form</h5>
141
+ <small class="text-muted">allowResubmit: false, resetOnSuccess: true</small>
142
+ </div>
143
+ <div class="card-body">
144
+ <form id="test-form-contact">
145
+ <div class="mb-3">
146
+ <label for="contact-message" class="form-label">Message</label>
147
+ <textarea class="form-control" id="contact-message" name="message" rows="2">Hello!</textarea>
148
+ </div>
149
+ <button type="submit" class="btn btn-primary">Send Message</button>
150
+ </form>
151
+ <div class="mt-2">
152
+ <small class="text-muted" id="contact-status">Status: ready</small>
153
+ </div>
154
+ </div>
155
+ </div>
156
+ </div>
157
+
158
+ <!-- Test 4: Manual Ready -->
159
+ <div class="col-lg-4">
160
+ <div class="card">
161
+ <div class="card-header">
162
+ <h5 class="mb-0">Test 4: Manual Ready</h5>
163
+ <small class="text-muted">autoReady: false, ready after 2 seconds</small>
164
+ </div>
165
+ <div class="card-body">
166
+ <form id="test-form-manual">
167
+ <div class="mb-3">
168
+ <label for="manual-data" class="form-label">Data</label>
169
+ <input type="text" class="form-control" id="manual-data" name="data">
170
+ </div>
171
+ <button type="submit" class="btn btn-primary">Submit</button>
172
+ </form>
173
+ <div class="mt-2">
174
+ <small class="text-muted" id="manual-status">Status: initializing</small>
175
+ </div>
176
+ </div>
177
+ </div>
178
+ </div>
179
+
180
+ </div>
181
+ </div>
@@ -63,6 +63,24 @@ module.exports = async function serve(complete) {
63
63
 
64
64
  // Write the config file
65
65
  jetpack.write('.temp/_config_browsersync.yml', `url: ${externalUrl}`);
66
+ // jetpack.write('.temp/_config_browsersync.yml', `
67
+ // url: ${externalUrl}
68
+
69
+ // web_manager:
70
+ // firebase:
71
+ // app:
72
+ // config:
73
+ // authDomain: "${new URL(externalUrl).host}"
74
+ // `);
75
+ // jetpack.write('.temp/_config_browsersync.yml', `
76
+ // url: ${externalUrl}
77
+
78
+ // web_manager:
79
+ // firebase:
80
+ // app:
81
+ // config:
82
+ // authDomain: "${new URL(localUrl).host}"
83
+ // `);
66
84
 
67
85
  // Set global variable to access browserSync in other files
68
86
  global.browserSync = browserSync;
@@ -1,5 +1,5 @@
1
1
  // Libraries
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
 
4
4
  // Logger class
5
5
  function Logger(name) {