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.
- package/CLAUDE.md +13 -0
- package/TODO.md +12 -4
- package/dist/assets/css/core/exit-popup.scss +17 -0
- package/dist/assets/css/core/utilities.scss +11 -3
- package/dist/assets/css/ultimate-jekyll-manager.scss +1 -1
- package/dist/assets/js/core/exit-popup.js +47 -9
- package/dist/assets/js/core/{page-loader.js → init.js} +15 -0
- package/dist/assets/js/libs/auth/pages.js +43 -36
- package/dist/assets/js/libs/form-manager.js +15 -10
- package/dist/assets/js/pages/admin/notifications/new/index.js +34 -12
- package/dist/assets/js/pages/blog/index.js +0 -2
- package/dist/assets/js/pages/contact/index.js +0 -2
- package/dist/assets/js/pages/download/index.js +0 -2
- package/dist/assets/js/ultimate-jekyll-manager.js +17 -21
- package/dist/assets/themes/classy/_theme.scss +2 -0
- package/dist/assets/themes/classy/css/base/_animations.scss +27 -0
- package/dist/assets/themes/classy/css/base/_backgrounds.scss +38 -0
- package/dist/assets/themes/classy/css/base/_spacing.scss +5 -0
- package/dist/assets/themes/classy/css/base/_typography.scss +0 -21
- package/dist/assets/themes/classy/css/components/_buttons.scss +19 -0
- package/dist/assets/themes/classy/css/components/_text.scss +31 -0
- package/dist/defaults/Gemfile +1 -1
- package/dist/defaults/dist/_includes/core/foot.html +58 -18
- package/dist/defaults/dist/_includes/themes/classy/backend/sections/topbar.html +1 -1
- package/dist/defaults/dist/_layouts/core/root.html +0 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/core/cover.html +3 -4
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html +201 -201
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/index.html +3 -3
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html +7 -4
- package/package.json +6 -6
- 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
|
-
|
|
114
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
|
@@ -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-
|
|
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
|
|
40
|
-
const $
|
|
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
|
-
$
|
|
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-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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:
|
|
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 || {},
|
|
@@ -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
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
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 {
|