ultimate-jekyll-manager 0.0.81 → 0.0.83

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 (31) hide show
  1. package/CLAUDE.md +13 -0
  2. package/TODO.md +12 -4
  3. package/dist/assets/css/core/exit-popup.scss +17 -0
  4. package/dist/assets/css/core/utilities.scss +11 -3
  5. package/dist/assets/css/ultimate-jekyll-manager.scss +1 -1
  6. package/dist/assets/js/core/exit-popup.js +47 -9
  7. package/dist/assets/js/core/{page-loader.js → init.js} +15 -0
  8. package/dist/assets/js/libs/auth/pages.js +43 -36
  9. package/dist/assets/js/libs/form-manager.js +15 -10
  10. package/dist/assets/js/pages/admin/notifications/new/index.js +34 -12
  11. package/dist/assets/js/pages/blog/index.js +0 -2
  12. package/dist/assets/js/pages/contact/index.js +0 -2
  13. package/dist/assets/js/pages/download/index.js +0 -2
  14. package/dist/assets/js/ultimate-jekyll-manager.js +17 -21
  15. package/dist/assets/themes/classy/_theme.scss +2 -0
  16. package/dist/assets/themes/classy/css/base/_animations.scss +27 -0
  17. package/dist/assets/themes/classy/css/base/_backgrounds.scss +38 -0
  18. package/dist/assets/themes/classy/css/base/_spacing.scss +5 -0
  19. package/dist/assets/themes/classy/css/base/_typography.scss +0 -21
  20. package/dist/assets/themes/classy/css/components/_buttons.scss +19 -0
  21. package/dist/assets/themes/classy/css/components/_text.scss +31 -0
  22. package/dist/defaults/Gemfile +1 -1
  23. package/dist/defaults/dist/_includes/core/foot.html +58 -18
  24. package/dist/defaults/dist/_includes/themes/classy/backend/sections/topbar.html +1 -1
  25. package/dist/defaults/dist/_layouts/core/root.html +0 -1
  26. package/dist/defaults/dist/_layouts/themes/classy/frontend/core/cover.html +3 -4
  27. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html +201 -201
  28. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/index.html +3 -3
  29. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html +7 -4
  30. package/package.json +6 -6
  31. package/dist/assets/css/core/buttons.scss +0 -52
package/CLAUDE.md CHANGED
@@ -39,6 +39,19 @@ src/assets/themes = Theme scss and js files that are can be picked and used by t
39
39
  DO NOT USE `bg-light`, `bg-dark`, `text-light`, or `text-dark`. We support BOTH light and dark mode, so instead use `bg-body`, `bg-body-secondary`, `bg-body-tertiary`, `text-body` and for buttons you can use `btn-adaptive` or `btn-outline-adaptive`.
40
40
  These classes adapt to light and dark mode automatically.
41
41
 
42
+ ## Libraries
43
+ I have some library preferences that I want you to follow:
44
+
45
+ ### WebManager
46
+ We use a custom library called `web-manager` to manage various aspects of the site. Please make yourself familiar with it by reviewing it: `/Users/ian/Developer/Repositories/ITW-Creative-Works/web-manager/src`
47
+ It offers various ultities like webManager.auth(), webManager.utilities(), webManager.sentry(), webManager.dom(). You should be using these utilities instead of writing your own code all the time.
48
+
49
+ ## FormManager
50
+ We use a custom library called `form-manager` to manage forms on the site. Please make yourself familiar with it by reviewing it: `/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-manager/src/assets/js/libs/form-manager.js`
51
+ * A good example of how to use it is the contact page
52
+ * HTML: `/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-manager/src/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html`
53
+ * JS: `/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-manager/src/assets/js/pages/contact/index.js`
54
+
42
55
  ## Audits
43
56
  I may ask you to help me fix problems identified by the AUDIT TASK (@src/gulp/tasks/audit.js)
44
57
  * In that case, please LOOK AT THE AUDIT FILES (Which I will provide the location to) and help me fix the issues ONE BY ONE.
package/TODO.md CHANGED
@@ -3,6 +3,10 @@ NEW TODO
3
3
  - service working failing to load scripts??
4
4
  - test SW on live site (and lighthouse)
5
5
  - cloudflare 1y cache on js and css (it's CB anyway so this will never matter)
6
+ - LOGIN NOT WORKING: email and password says its wrong, google just takes you back to the signin page and nothing happens
7
+ - FIx formmanager .getData() it keeps returning an empty object, make it work flawlessyly
8
+ - It keeps testing whether there is a "." every time it reads or writes. just call the getNested and setNested functions and then check for the DOT inside those functions
9
+ - form manager should NOT submit if the button that was clicked is disabled (class or attribute)
6
10
 
7
11
  Make an admin dashboard backend
8
12
 
@@ -17,6 +21,10 @@ Admin dashboard backend pages
17
21
  * Create the product in stripe, paypal, etc
18
22
 
19
23
 
24
+ REVIEW page
25
+ - Make a rewarded review page. When the user lands there, it explains that they COULD win $25 Amazon giftcard for writing a reivew. They upload the screenshot then it sends them to a fake countdown page where 3 winners are picked every week. So it conts down and tells them to refresht he page. At the end of the week it picks 3 random winners but it should never axtually pick anyone and just tell them to try again next week. But it should look legit.
26
+
27
+
20
28
  MIGRATIONS
21
29
 
22
30
  TEST:
@@ -110,10 +118,8 @@ user:sign-up fetch request has new structure including context
110
118
  ---------
111
119
 
112
120
 
113
- INCLUDE THIS in css build process
114
- - src/defaults/dist/_includes/master/assets/css/defaults.css
115
- - src/defaults/dist/_includes/master/assets/css/cookieconsent.css
116
- - social buttons too?? or make our own??
121
+
122
+
117
123
 
118
124
  THINGS TO ADD
119
125
  - auto cached translations
@@ -141,6 +147,8 @@ Terms of service
141
147
  - https://sentry.io/terms/
142
148
  - https://docs.github.com/en/site-policy/github-terms/github-terms-of-service#the-github-terms-of-service
143
149
  - https://help.instagram.com/termsofuse
150
+ - https://discord.com/terms
151
+ - Update arbitration: https://www.equifax.com/terms/
144
152
 
145
153
  prechat-btn
146
154
  - should have width and height set to 0px INLINE so it doesnt appear HUGE when loading
@@ -0,0 +1,17 @@
1
+ // Simple wave animation for the hand icon
2
+ .animation-hand-wave {
3
+ animation: wave 1s ease-in-out infinite;
4
+ transform-origin: 70% 70%;
5
+ }
6
+
7
+ // Keyframe animation for waving hand
8
+ @keyframes wave {
9
+ 0%, 100% { transform: rotate(0deg); }
10
+ 10%, 30% { transform: rotate(14deg); }
11
+ 20%, 40% { transform: rotate(-8deg); }
12
+ 50% { transform: rotate(14deg); }
13
+ 60% { transform: rotate(-4deg); }
14
+ 70% { transform: rotate(14deg); }
15
+ 80% { transform: rotate(-4deg); }
16
+ 90% { transform: rotate(10deg); }
17
+ }
@@ -7,12 +7,20 @@
7
7
 
8
8
  // Properly hide things
9
9
  .hidden, [hidden] {
10
- display: none !important
10
+ display: none !important;
11
11
  }
12
12
 
13
- // Show disabled mouse on disabled things
14
- .disabled, [disabled] {
13
+ .disabled,
14
+ [disabled],
15
+ :disabled {
15
16
  cursor: not-allowed !important;
17
+ pointer-events: all !important;
18
+
19
+ // Disable pointer events on all children inside disabled buttons and links
20
+ // This helps fix event bubbling issues for click listeners
21
+ * {
22
+ pointer-events: none !important;
23
+ }
16
24
  }
17
25
 
18
26
  // Prevent pointer events on all children inside buttons and links
@@ -16,8 +16,8 @@
16
16
  @use 'core/initialize' as *;
17
17
  @use 'core/utilities' as *;
18
18
  @use 'core/animations' as *;
19
- @use 'core/buttons' as *;
20
19
  @use 'core/alert' as *;
20
+ @use 'core/exit-popup' as *;
21
21
 
22
22
  // Import page-specific styles
23
23
  @use '_page-specific' as *;
@@ -23,24 +23,44 @@ export default function (Manager, options) {
23
23
  setupMouseLeaveDetection();
24
24
  });
25
25
 
26
+ // Make this available on window object in DEV mode for testing
27
+ /* @dev-only:start */
28
+ {
29
+ window.showExitPopup = showExitPopup;
30
+ }
31
+ /* @dev-only:end */
32
+
26
33
  function updateModalContent($modal) {
27
34
  // Update title
28
- const $title = $modal.querySelector('.modal-title');
35
+ const $title = $modal.querySelector('.modal-exit-title');
29
36
  if ($title && config.title) {
30
37
  $title.textContent = config.title;
31
38
  }
32
39
 
33
- // Update message
34
- const $message = $modal.querySelector('.modal-body p');
40
+ // Update main message
41
+ const $message = $modal.querySelector('.modal-exit-message');
35
42
  if ($message && config.message) {
36
43
  $message.textContent = config.message;
37
44
  }
38
45
 
39
- // Update button
40
- const $button = $modal.querySelector('.modal-footer .btn-primary');
46
+ // Update offer title
47
+ const $offerTitleText = $modal.querySelector('.modal-exit-offer-title-text');
48
+ if ($offerTitleText && config.offerTitle) {
49
+ $offerTitleText.textContent = config.offerTitle;
50
+ }
51
+
52
+ // Update offer description
53
+ const $offerDesc = $modal.querySelector('.modal-exit-offer-description');
54
+ if ($offerDesc && config.offerDescription) {
55
+ $offerDesc.textContent = config.offerDescription;
56
+ }
57
+
58
+ // Update main button
59
+ const $button = $modal.querySelector('.modal-exit-button');
60
+ const $buttonText = $modal.querySelector('.modal-exit-button-text');
41
61
  if ($button && config.okButton) {
42
- if (config.okButton.text) {
43
- $button.textContent = config.okButton.text;
62
+ if ($buttonText && config.okButton.text) {
63
+ $buttonText.textContent = config.okButton.text;
44
64
  }
45
65
  if (config.okButton.link) {
46
66
  // Add UTM parameters to track exit popup conversions
@@ -52,6 +72,12 @@ export default function (Manager, options) {
52
72
  }
53
73
  }
54
74
 
75
+ // Update "Maybe later" link text if configured
76
+ const $dismissLink = $modal.querySelector('.modal-exit-dismiss');
77
+ if ($dismissLink && config.dismissText) {
78
+ $dismissLink.textContent = config.dismissText;
79
+ }
80
+
55
81
  // Remove hidden attribute to make modal available
56
82
  $modal.removeAttribute('hidden');
57
83
  }
@@ -116,11 +142,16 @@ export default function (Manager, options) {
116
142
  try {
117
143
  modal.show();
118
144
 
145
+ // Add fade class after modal is shown to avoid conflicts with animation-slide-up
146
+ $modalElement.addEventListener('shown.bs.modal', () => {
147
+ $modalElement.classList.add('fade');
148
+ }, { once: true });
149
+
119
150
  // Track exit popup shown
120
151
  trackExitPopupShown();
121
152
 
122
- // Track button clicks on the exit popup
123
- const $button = $modalElement.querySelector('.modal-footer .btn');
153
+ // Track button clicks on the exit popup (main CTA button)
154
+ const $button = $modalElement.querySelector('.modal-exit-button');
124
155
  if ($button && !$button.hasAttribute('data-exit-popup-tracked')) {
125
156
  $button.setAttribute('data-exit-popup-tracked', 'true');
126
157
  $button.addEventListener('click', () => {
@@ -128,6 +159,13 @@ export default function (Manager, options) {
128
159
  });
129
160
  }
130
161
 
162
+ // Remove focus from any focused element before hiding to prevent aria-hidden warning
163
+ $modalElement.addEventListener('hide.bs.modal', () => {
164
+ if (document.activeElement && $modalElement.contains(document.activeElement)) {
165
+ document.activeElement.blur();
166
+ }
167
+ }, { once: true });
168
+
131
169
  // Track modal dismiss
132
170
  $modalElement.addEventListener('hidden.bs.modal', () => {
133
171
  trackExitPopupDismissed();
@@ -10,6 +10,21 @@ export default function (Manager, options) {
10
10
  }, 16);
11
11
  };
12
12
 
13
+ // Attach click listener to prevent clicks on disabled elements
14
+ document.addEventListener('click', (e) => {
15
+ const $target = e.target;
16
+ if ($target.closest('*[disabled], *.disabled, :disabled')) {
17
+ // Log disabled click attempt
18
+ console.log('Click prevented (disabled):', $target);
19
+
20
+ // Prevent all actions
21
+ e.preventDefault();
22
+ e.stopImmediatePropagation();
23
+ e.stopPropagation();
24
+ return false;
25
+ }
26
+ }, { capture: true });
27
+
13
28
  // Check if the window is already loaded
14
29
  if (document.readyState === 'complete') {
15
30
  // Already loaded, remove immediately
@@ -9,9 +9,15 @@ export default function (Manager) {
9
9
  // Form manager instance
10
10
  let formManager = null;
11
11
 
12
+ // Shared FormManager configuration
13
+ const formManagerConfig = {
14
+ allowMultipleSubmissions: false,
15
+ validateOnSubmit: true,
16
+ };
17
+
12
18
  // Check query string for popup parameter
13
19
  const url = new URL(window.location.href);
14
- const useAuthPopup = url.searchParams.get('authPopup') === 'true';
20
+ const useAuthPopup = url.searchParams.get('authPopup') === 'true' || window !== window.top;
15
21
 
16
22
  // Handle DOM ready
17
23
  webManager.dom().ready()
@@ -58,11 +64,8 @@ export default function (Manager) {
58
64
  // Initialize signin form
59
65
  function initializeSigninForm() {
60
66
  formManager = new FormManager('#signin-form', {
61
- submitButtonLoadingText: 'Signing in...',
62
- allowMultipleSubmissions: false,
63
- validateOnSubmit: false, // We'll handle validation conditionally
64
- fieldErrorClass: 'is-invalid',
65
- fieldSuccessClass: 'is-valid'
67
+ ...formManagerConfig,
68
+ submitButtonLoadingText: 'Signing in...'
66
69
  });
67
70
 
68
71
  // Handle form submission
@@ -85,7 +88,8 @@ export default function (Manager) {
85
88
 
86
89
  try {
87
90
  if (provider === 'email') {
88
- await handleEmailSignin();
91
+ // Pass the already collected data to avoid re-collecting from disabled fields
92
+ await handleEmailSignin(data);
89
93
  } else if (provider) {
90
94
  await signInWithProvider(provider, 'signin');
91
95
  }
@@ -105,11 +109,8 @@ export default function (Manager) {
105
109
  // Initialize signup form
106
110
  function initializeSignupForm() {
107
111
  formManager = new FormManager('#signup-form', {
108
- submitButtonLoadingText: 'Creating account...',
109
- allowMultipleSubmissions: false,
110
- validateOnSubmit: false, // We'll handle validation conditionally
111
- fieldErrorClass: 'is-invalid',
112
- fieldSuccessClass: 'is-valid'
112
+ ...formManagerConfig,
113
+ submitButtonLoadingText: 'Creating account...'
113
114
  });
114
115
 
115
116
  // Handle form submission
@@ -132,7 +133,8 @@ export default function (Manager) {
132
133
 
133
134
  try {
134
135
  if (provider === 'email') {
135
- await handleEmailSignup();
136
+ // Pass the already collected data to avoid re-collecting from disabled fields
137
+ await handleEmailSignup(data);
136
138
  } else if (provider) {
137
139
  await signInWithProvider(provider, 'signup');
138
140
  }
@@ -152,12 +154,9 @@ export default function (Manager) {
152
154
  // Initialize reset form
153
155
  function initializeResetForm() {
154
156
  formManager = new FormManager('#reset-form', {
157
+ ...formManagerConfig,
155
158
  submitButtonLoadingText: 'Sending...',
156
- submitButtonSuccessText: 'Email Sent!',
157
- allowMultipleSubmissions: false,
158
- validateOnSubmit: true,
159
- fieldErrorClass: 'is-invalid',
160
- fieldSuccessClass: 'is-valid'
159
+ submitButtonSuccessText: 'Email Sent!'
161
160
  });
162
161
 
163
162
  // Handle form submission
@@ -165,8 +164,11 @@ export default function (Manager) {
165
164
  // Prevent FormManager's default submit handler
166
165
  e.preventDefault();
167
166
 
167
+ const { data } = e.detail;
168
+
168
169
  try {
169
- await handlePasswordReset();
170
+ // Pass the already collected data to avoid re-collecting from disabled fields
171
+ await handlePasswordReset(data);
170
172
  } catch (error) {
171
173
  // Set form back to ready state
172
174
  formManager.setFormState('ready');
@@ -330,9 +332,8 @@ export default function (Manager) {
330
332
  return userCredential;
331
333
  }
332
334
 
333
- async function handleEmailSignup() {
334
- // Get form data from FormManager (already validated by FormManager)
335
- const formData = formManager.getData();
335
+ async function handleEmailSignup(formData) {
336
+ // Use the form data passed from the submit event (already validated)
336
337
 
337
338
  // Sanitize email by trimming
338
339
  const email = formData.email?.trim() || '';
@@ -384,14 +385,16 @@ export default function (Manager) {
384
385
  }
385
386
  }
386
387
 
387
- async function handleEmailSignin() {
388
- // Get form data from FormManager (already validated by FormManager)
389
- const formData = formManager.getData();
388
+ async function handleEmailSignin(formData) {
389
+ // Use the form data passed from the submit event (already validated)
390
390
 
391
391
  // Sanitize email by trimming
392
392
  const email = formData.email?.trim() || '';
393
393
  const password = formData.password || '';
394
394
 
395
+ // Log
396
+ console.log('Attempting email sign-in for:', email);
397
+
395
398
  // Sign in with email and password
396
399
  const userCredential = await attemptEmailSignIn(email, password);
397
400
 
@@ -405,9 +408,8 @@ export default function (Manager) {
405
408
  formManager.setFormState('submitted');
406
409
  }
407
410
 
408
- async function handlePasswordReset() {
409
- // Get form data from FormManager (already validated by FormManager)
410
- const formData = formManager.getData();
411
+ async function handlePasswordReset(formData) {
412
+ // Use the form data passed from the submit event (already validated)
411
413
 
412
414
  // Sanitize email by trimming
413
415
  const email = formData.email?.trim() || '';
@@ -462,10 +464,6 @@ export default function (Manager) {
462
464
 
463
465
  const auth = getAuth();
464
466
 
465
- // Log auth configuration for debugging
466
- console.log('[Debug] Auth domain:', auth.config.authDomain);
467
- console.log('[Debug] API Key:', auth.config.apiKey ? 'Set' : 'Not set');
468
-
469
467
  let provider;
470
468
 
471
469
  // Create provider based on provider name
@@ -486,10 +484,19 @@ export default function (Manager) {
486
484
  throw new Error(`Unsupported provider: ${providerName}`);
487
485
  }
488
486
 
489
- // Log current domain for debugging
490
- console.log('[Debug] Current domain:', window.location.hostname);
491
- console.log('[Debug] Current origin:', window.location.origin);
492
- console.log('[Debug] Auth method:', useAuthPopup ? 'popup' : 'redirect');
487
+ // Show warning in dev mode when using redirect
488
+ if (webManager.isDevelopment() && !useAuthPopup) {
489
+ webManager.utilities().showNotification(
490
+ 'OAuth redirect may fail in development. Use localhost:4000 or add ?authPopup=true to the URL',
491
+ {
492
+ type: 'warning',
493
+ timeout: 10000 // Show for 10 seconds
494
+ }
495
+ );
496
+
497
+ // Wait
498
+ await new Promise(resolve => setTimeout(resolve, 1000));
499
+ }
493
500
 
494
501
  // Use popup if query parameter is set, otherwise use redirect
495
502
  if (useAuthPopup) {
@@ -172,16 +172,29 @@ export class FormManager extends EventTarget {
172
172
  // Collect form data
173
173
  const formData = this.collectFormData();
174
174
 
175
+ // Build the submit event detail
176
+ const submitEvent = new CustomEvent('submit', {
177
+ detail: {
178
+ data: formData,
179
+ form: this.form,
180
+ submitButton: this.clickedSubmitButton
181
+ },
182
+ cancelable: true
183
+ });
184
+
175
185
  /* @dev-only:start */
176
186
  {
177
- console.log(`[FormManager] Submit event triggered on ${this.form.id || 'form'}`, formData);
187
+ console.log(`[FormManager] Submit event triggered on ${this.form.id}`, formData, submitEvent.detail.submitButton);
178
188
  }
179
189
  /* @dev-only:end */
180
190
 
181
191
  // Validate if enabled
192
+ console.log('-----1');
182
193
  if (this.config.validateOnSubmit) {
194
+ console.log('-----2');
183
195
  const validation = this.validate(formData);
184
196
  if (!validation.isValid) {
197
+ console.log('-----3');
185
198
  this.showErrors(validation.errors);
186
199
  // Show a summary notification for validation errors
187
200
  const errorCount = Object.keys(validation.errors).length;
@@ -197,15 +210,6 @@ export class FormManager extends EventTarget {
197
210
  this.setFormState('submitting');
198
211
 
199
212
  // Emit submit event with the clicked submit button
200
- const submitEvent = new CustomEvent('submit', {
201
- detail: {
202
- data: formData,
203
- form: this.form,
204
- submitButton: this.clickedSubmitButton
205
- },
206
- cancelable: true
207
- });
208
-
209
213
  this.dispatchEvent(submitEvent);
210
214
 
211
215
  // Reset clicked button after dispatching event
@@ -511,6 +515,7 @@ export class FormManager extends EventTarget {
511
515
  const formData = new FormData(this.form);
512
516
  const data = {};
513
517
 
518
+
514
519
  // Convert FormData to plain object with support for dot notation
515
520
  for (const [key, value] of formData.entries()) {
516
521
  // Check if key contains dots for nested structure
@@ -89,6 +89,11 @@ function initializeUI() {
89
89
  // Title character counter
90
90
  const $titleInput = document.querySelector('input[name="notification.title"]');
91
91
  if ($titleInput) {
92
+ // Set default value in dev mode
93
+ if (webManager.isDevelopment()) {
94
+ $titleInput.value = 'Test Notification Title';
95
+ }
96
+
92
97
  $titleInput.addEventListener('input', (e) => {
93
98
  const length = e.target.value.length;
94
99
  const $titleCount = document.querySelector('#title-char-count');
@@ -102,6 +107,11 @@ function initializeUI() {
102
107
  // Body character counter
103
108
  const $bodyInput = document.querySelector('textarea[name="notification.body"]');
104
109
  if ($bodyInput) {
110
+ // Set default value in dev mode
111
+ if (webManager.isDevelopment()) {
112
+ $bodyInput.value = 'This is a test notification body message for development.';
113
+ }
114
+
105
115
  $bodyInput.addEventListener('input', (e) => {
106
116
  const length = e.target.value.length;
107
117
  const $bodyCount = document.querySelector('#body-char-count');
@@ -174,10 +184,8 @@ function handleFormChange(event) {
174
184
 
175
185
  // Handle form submission
176
186
  async function handleSubmit(event) {
177
- const formData = formManager.getData();
178
-
179
- // Log
180
- console.log('Submitting notification with data:', formData);
187
+ // Get data from event detail if available, otherwise from formManager
188
+ const formData = event.detail?.data || formManager.getData();
181
189
 
182
190
  try {
183
191
  // Transform data for API
@@ -189,6 +197,7 @@ async function handleSubmit(event) {
189
197
  filtered: { total: stats.filteredUsers }
190
198
  };
191
199
 
200
+
192
201
  // Call API
193
202
  const response = await sendNotification(payload);
194
203
 
@@ -207,25 +216,38 @@ async function handleSubmit(event) {
207
216
 
208
217
  // Transform data for API
209
218
  function transformDataForAPI(formData) {
219
+ console.log('[Debug] transformDataForAPI called');
220
+ console.log('[Debug] formData received:', formData);
221
+ console.log('[Debug] formData.notification:', formData.notification);
222
+
210
223
  const now = new Date();
211
224
  const notification = formData.notification || {};
212
225
 
226
+ console.log('[Debug] notification object:', notification);
227
+ console.log('[Debug] notification.clickAction:', notification.clickAction);
228
+ console.log('[Debug] typeof notification:', typeof notification);
229
+ console.log('[Debug] Object.keys(notification):', Object.keys(notification));
230
+
213
231
  // Generate ID
214
232
  const id = now.getTime();
215
233
 
216
234
  // Build click action URL with tracking
217
- const clickActionUrl = new URL('https://promo-server.itwcreativeworks.com/redirect/notification');
218
- clickActionUrl.searchParams.set('id', id);
219
- clickActionUrl.searchParams.set('type', 'notification');
220
- clickActionUrl.searchParams.set('url', notification.clickAction);
235
+ const redirectUrl = new URL('https://promo-server.itwcreativeworks.com/redirect/notification');
236
+ redirectUrl.searchParams.set('id', id);
237
+ redirectUrl.searchParams.set('type', 'notification');
238
+
239
+ // Make sure we have the actual clickAction value
240
+ const clickActionValue = notification.clickAction || '';
241
+ console.log('[Debug] clickActionValue to be set in URL:', clickActionValue);
242
+ redirectUrl.searchParams.set('url', clickActionValue);
221
243
 
222
244
  return {
223
245
  id: id,
224
246
  notification: {
225
- icon: notification.icon,
226
- title: notification.title,
227
- body: notification.body,
228
- clickAction: clickActionUrl.toString()
247
+ icon: notification.icon || '',
248
+ title: notification.title || '',
249
+ body: notification.body || '',
250
+ clickAction: redirectUrl.toString()
229
251
  },
230
252
  created: now.toISOString(),
231
253
  channels: formData.channels || {},
@@ -39,8 +39,6 @@ function setupNewsletterForm() {
39
39
  resetOnSuccess: true,
40
40
  submitButtonLoadingText: 'Subscribing...',
41
41
  submitButtonSuccessText: 'Subscribed!',
42
- fieldErrorClass: 'is-invalid',
43
- fieldSuccessClass: 'is-valid'
44
42
  });
45
43
 
46
44
  // Listen to FormManager events
@@ -33,8 +33,6 @@ function setupForm() {
33
33
  resetOnSuccess: true,
34
34
  submitButtonLoadingText: 'Sending...',
35
35
  submitButtonSuccessText: 'Message Sent!',
36
- fieldErrorClass: 'is-invalid',
37
- fieldSuccessClass: 'is-valid'
38
36
  });
39
37
 
40
38
  // Listen to FormManager events
@@ -300,8 +300,6 @@ function setupMobileEmailForms() {
300
300
  allowMultipleSubmissions: false,
301
301
  submitButtonLoadingText: 'Sending...',
302
302
  submitButtonSuccessText: 'Sent!',
303
- fieldErrorClass: 'is-invalid',
304
- fieldSuccessClass: 'is-valid'
305
303
  });
306
304
 
307
305
  formManager.addEventListener('submit', (e) => handleMobileEmailSubmit(e, platform));
@@ -1,3 +1,10 @@
1
+ // Static imports for core modules (bundled together for efficiency)
2
+ import initModule from '__main_assets__/js/core/init.js';
3
+ import authModule from '__main_assets__/js/core/auth.js';
4
+ import lazyLoadingModule from '__main_assets__/js/core/lazy-loading.js';
5
+ import queryStringsModule from '__main_assets__/js/core/query-strings.js';
6
+ import serviceWorkerModule from '__main_assets__/js/core/service-worker.js';
7
+
1
8
  // Ultimate Jekyll Manager Module
2
9
  export default async function (Manager, options) {
3
10
  // Shortcuts
@@ -9,16 +16,14 @@ export default async function (Manager, options) {
9
16
  // Log
10
17
  console.log('Global module loaded successfully (assets/js/ultimate-jekyll-manager.js)');
11
18
 
12
- // Core modules to always load
13
- const fixedModules = [
14
- 'page-loader.js',
15
- 'auth.js',
16
- 'lazy-loading.js',
17
- 'query-strings.js',
18
- 'service-worker.js'
19
- ];
19
+ // Initialize fixed modules synchronously (already loaded via static imports)
20
+ initModule(Manager, options);
21
+ authModule(Manager, options);
22
+ lazyLoadingModule(Manager, options);
23
+ queryStringsModule(Manager, options);
24
+ serviceWorkerModule(Manager, options);
20
25
 
21
- // Conditionally loaded modules based on config
26
+ // Conditionally loaded modules based on config (keep as dynamic imports)
22
27
  const conditionalModules = [
23
28
  { path: 'chatsy.js', configKey: 'chatsy' },
24
29
  { path: 'cookieconsent.js', configKey: 'cookieConsent' },
@@ -26,18 +31,9 @@ export default async function (Manager, options) {
26
31
  { path: 'social-sharing.js', configKey: 'socialSharing' }
27
32
  ];
28
33
 
29
- // Load all modules in parallel
34
+ // Load conditional modules in parallel
30
35
  const modulePromises = [];
31
36
 
32
- // Add fixed modules
33
- for (const modulePath of fixedModules) {
34
- modulePromises.push(
35
- import(`__main_assets__/js/core/${modulePath}`)
36
- .then(({ default: moduleFunc }) => moduleFunc(Manager, options))
37
- .catch(error => console.error(`Failed to load ${modulePath}:`, error))
38
- );
39
- }
40
-
41
37
  // Add conditional modules if enabled
42
38
  for (const module of conditionalModules) {
43
39
  const moduleConfig = webManager.config[module.configKey];
@@ -52,12 +48,12 @@ export default async function (Manager, options) {
52
48
  }
53
49
  }
54
50
 
55
- // Add theme loading
51
+ // Add theme loading (keep as dynamic import since themes can vary)
56
52
  modulePromises.push(
57
53
  import('__theme__/_theme.js')
58
54
  .catch(error => console.error('Failed to load theme:', error))
59
55
  );
60
56
 
61
- // Wait for all modules to load
57
+ // Wait for all conditional modules to load
62
58
  await Promise.all(modulePromises);
63
59
  }
@@ -46,6 +46,7 @@ $border-radius-pill: 50rem;
46
46
  @import 'css/base/variables';
47
47
 
48
48
  // Import base styles
49
+ @import 'css/base/animations';
49
50
  @import 'css/base/backgrounds';
50
51
  @import 'css/base/borders';
51
52
  @import 'css/base/soft-colors';
@@ -69,6 +70,7 @@ $border-radius-pill: 50rem;
69
70
  @import 'css/components/links';
70
71
  @import 'css/components/logo-scroll';
71
72
  @import 'css/components/spinners';
73
+ @import 'css/components/text';
72
74
 
73
75
  // Global theme improvements
74
76
  body {