ultimate-jekyll-manager 0.0.118 → 0.0.120

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 (51) hide show
  1. package/CLAUDE.md +409 -23
  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/core/auth.js +5 -4
  6. package/dist/assets/js/core/cookieconsent.js +24 -17
  7. package/dist/assets/js/core/exit-popup.js +15 -12
  8. package/dist/assets/js/core/social-sharing.js +8 -4
  9. package/dist/assets/js/libs/auth/pages.js +78 -149
  10. package/dist/assets/js/libs/dev.js +192 -129
  11. package/dist/assets/js/libs/form-manager.js +643 -775
  12. package/dist/assets/js/pages/account/index.js +3 -2
  13. package/dist/assets/js/pages/account/sections/api-keys.js +37 -52
  14. package/dist/assets/js/pages/account/sections/connections.js +37 -46
  15. package/dist/assets/js/pages/account/sections/delete.js +57 -78
  16. package/dist/assets/js/pages/account/sections/profile.js +37 -56
  17. package/dist/assets/js/pages/account/sections/security.js +102 -125
  18. package/dist/assets/js/pages/admin/notifications/new/index.js +73 -151
  19. package/dist/assets/js/pages/blog/index.js +33 -53
  20. package/dist/assets/js/pages/contact/index.js +112 -173
  21. package/dist/assets/js/pages/download/index.js +39 -86
  22. package/dist/assets/js/pages/oauth2/index.js +17 -17
  23. package/dist/assets/js/pages/payment/checkout/index.js +23 -36
  24. package/dist/assets/js/pages/pricing/index.js +5 -2
  25. package/dist/assets/js/pages/test/libraries/form-manager/index.js +194 -0
  26. package/dist/assets/themes/classy/css/components/_cards.scss +2 -2
  27. package/dist/defaults/_.env +6 -0
  28. package/dist/defaults/_.gitignore +7 -1
  29. package/dist/defaults/dist/_includes/core/body.html +5 -13
  30. package/dist/defaults/dist/_includes/core/foot.html +1 -0
  31. package/dist/defaults/dist/_includes/themes/classy/frontend/sections/nav.html +51 -36
  32. package/dist/defaults/dist/_layouts/blueprint/admin/notifications/new.html +13 -2
  33. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/about.html +84 -42
  34. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html +26 -21
  35. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signin.html +2 -2
  36. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signup.html +2 -2
  37. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/index.html +72 -58
  38. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/post.html +46 -29
  39. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html +46 -53
  40. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/download.html +111 -73
  41. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/index.html +111 -56
  42. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html +127 -81
  43. package/dist/defaults/dist/pages/test/libraries/form-manager.html +181 -0
  44. package/dist/defaults/dist/pages/test/libraries/lazy-loading.html +1 -1
  45. package/dist/gulp/tasks/defaults.js +210 -1
  46. package/dist/gulp/tasks/serve.js +18 -0
  47. package/dist/lib/logger.js +1 -1
  48. package/firebase-debug.log +770 -0
  49. package/package.json +6 -6
  50. package/.playwright-mcp/page-2025-10-22T19-11-27-666Z.png +0 -0
  51. 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
@@ -196,8 +196,9 @@ function trackPricingToggle(billingType) {
196
196
  content_category: billingType
197
197
  });
198
198
  ttq.track('ViewContent', {
199
- content_name: 'Pricing Toggle',
200
- content_type: billingType
199
+ content_id: 'pricing-page',
200
+ content_type: 'product',
201
+ content_name: 'Pricing Toggle'
201
202
  });
202
203
  }
203
204
 
@@ -247,6 +248,8 @@ function trackEnterpriseContact() {
247
248
  content_name: 'Enterprise Plan'
248
249
  });
249
250
  ttq.track('Contact', {
251
+ content_id: 'enterprise-plan',
252
+ content_type: 'product',
250
253
  content_name: 'Enterprise Plan'
251
254
  });
252
255
  }
@@ -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
+ }
@@ -11,8 +11,8 @@
11
11
  box-shadow: $classy-shadow-sm;
12
12
  position: relative;
13
13
 
14
- // Allow badges to overflow
15
- &:has(.badge) {
14
+ // Allow badges to overflow (except when overflow-hidden is explicitly set)
15
+ &:has(.badge):not(.overflow-hidden) {
16
16
  overflow: visible !important;
17
17
  }
18
18
 
@@ -0,0 +1,6 @@
1
+ # ========== Default Values ==========
2
+ # ...
3
+
4
+ # ========== Custom Values ==========
5
+ # Add your custom environment variables below this line
6
+ # ...
@@ -1,4 +1,7 @@
1
+ # ========== Default Values ==========
2
+ # macOS
1
3
  .DS_Store
4
+
2
5
  # Logs
3
6
  logs
4
7
  *.log
@@ -11,7 +14,6 @@ firebase-debug.log*
11
14
  .firebase/
12
15
 
13
16
  # Firebase config
14
-
15
17
  # Uncomment this if you'd like others to create their own Firebase project.
16
18
  # For a team working on the same Firebase project(s), it is recommended to leave
17
19
  # it commented so all members can deploy to the same project(s) in .firebaserc.
@@ -77,3 +79,7 @@ node_modules/
77
79
  /.cache
78
80
  /_legacy
79
81
  START-COMMANDS.md
82
+
83
+ # ========== Custom Values ==========
84
+ # Add your custom ignore patterns below this line
85
+ # ...
@@ -9,7 +9,7 @@
9
9
  hidden>
10
10
  <span class="main-alert-close">&times;</span>
11
11
  <div>
12
- <i class="fa-solid fa-warning fa-bounce me-2"></i>
12
+ {% uj_icon "warning", "fa-bounce me-2" %}
13
13
  You are using an outdated browser that our site <strong>DOES NOT</strong> support. Please <a href="https://www.google.com/chrome" rel="nofollow" target="_blank">click here</a> to update your browser.
14
14
  </div>
15
15
  </div>
@@ -19,11 +19,11 @@
19
19
  hidden>
20
20
  <span class="main-alert-close">&times;</span>
21
21
  <div>
22
- <i class="fa-solid fa-warning fa-bounce me-2"></i>
22
+ {% uj_icon "warning", "fa-bounce me-2" %}
23
23
  There is a <strong>problem with your payment method</strong>. To continue using <strong>{{ site.brand.name }}</strong>, please <a href="{{ site.url }}/account#billing" target="_blank">update your payment method</a>.
24
24
  </div>
25
25
  </div>
26
- <div
26
+ <!-- <div
27
27
  class="main-alert main-alert-top main-alert-fixed main-alert-sale bg-primary animation-fade-in"
28
28
  role="alert" aria-live="polite" aria-label="Flash sale"
29
29
  hidden>
@@ -33,17 +33,9 @@
33
33
  <strong>FLASH SALE!</strong>
34
34
  Save <strong>15%</strong> at checkout—today only! <a href="{{ site.url }}/pricing" target="_blank">Claim discount</a>.
35
35
  </div>
36
- </div>
36
+ </div> -->
37
37
 
38
- {%- comment -%}
39
- Icon Pre-rendering System
40
- Pages can specify which icons to pre-render in their frontmatter:
41
- prerender_icons:
42
- - name: "apple"
43
- class: "fa-3xl"
44
- - name: "android"
45
- class: "fa-2xl"
46
- {%- endcomment -%}
38
+ <!-- Prerendered Icons -->
47
39
  {%- assign icons = page.resolved.prerender_icons | default: empty -%}
48
40
  {%- iftruthy icons -%}
49
41
  <!-- Pre-rendered Icon Templates -->
@@ -69,6 +69,7 @@
69
69
  buildTime: {{ site.uj.cache_breaker }},
70
70
  brand: {{ page.resolved.brand | jsonify }},
71
71
  advertising: {{ page.resolved.advertising | jsonify }},
72
+ tracking: {{ page.resolved.tracking | jsonify }},
72
73
  {% for item in page.resolved.web_manager %}
73
74
  {{ item[0] | jsonify }}: {{ item[1] | jsonify }},
74
75
  {% endfor %}
@@ -130,18 +130,23 @@
130
130
  <span>{{ child.label }}</span>
131
131
  </span>
132
132
  {% endcapture %}
133
- <li>
134
- {% iftruthy child.href %}
135
- <a class="{{ child_class }}" href="{{ child.href }}" {{ child_attributes }}>
136
- {{ child_content }}
137
- </a>
138
- {% endiftruthy %}
139
- {% iffalsy child.href %}
140
- <button class="{{ child_class }}" type="button" {{ child_attributes }}>
141
- {{ child_content }}
142
- </button>
143
- {% endiffalsy %}
144
- </li>
133
+
134
+ {% if child.divider %}
135
+ <li><hr class="dropdown-divider" {{ child_attributes }}/></li>
136
+ {% else %}
137
+ <li>
138
+ {% iftruthy child.href %}
139
+ <a class="{{ child_class }}" href="{{ child.href }}" {{ child_attributes }}>
140
+ {{ child_content }}
141
+ </a>
142
+ {% endiftruthy %}
143
+ {% iffalsy child.href %}
144
+ <button class="{{ child_class }}" type="button" {{ child_attributes }}>
145
+ {{ child_content }}
146
+ </button>
147
+ {% endiffalsy %}
148
+ </li>
149
+ {% endif %}
145
150
  {% endfor %}
146
151
  </ul>
147
152
  </li>
@@ -272,18 +277,23 @@
272
277
  <span>{{ child.label }}</span>
273
278
  </span>
274
279
  {% endcapture %}
275
- <li>
276
- {% iftruthy child.href %}
277
- <a class="{{ child_class }}" href="{{ child.href }}" {{ child_attributes }}>
278
- {{ child_content }}
279
- </a>
280
- {% endiftruthy %}
281
- {% iffalsy child.href %}
282
- <button class="{{ child_class }}" type="button" {{ child_attributes }}>
283
- {{ child_content }}
284
- </button>
285
- {% endiffalsy %}
286
- </li>
280
+
281
+ {% if child.divider %}
282
+ <li><hr class="dropdown-divider" {{ child_attributes }}/></li>
283
+ {% else %}
284
+ <li>
285
+ {% iftruthy child.href %}
286
+ <a class="{{ child_class }}" href="{{ child.href }}" {{ child_attributes }}>
287
+ {{ child_content }}
288
+ </a>
289
+ {% endiftruthy %}
290
+ {% iffalsy child.href %}
291
+ <button class="{{ child_class }}" type="button" {{ child_attributes }}>
292
+ {{ child_content }}
293
+ </button>
294
+ {% endiffalsy %}
295
+ </li>
296
+ {% endif %}
287
297
  {% endfor %}
288
298
  </ul>
289
299
  </div>
@@ -343,18 +353,23 @@
343
353
  <span>{{ child.label }}</span>
344
354
  </span>
345
355
  {% endcapture %}
346
- <li>
347
- {% iftruthy child.href %}
348
- <a class="{{ child_class }}" href="{{ child.href }}" {{ child_attributes }}>
349
- {{ child_content }}
350
- </a>
351
- {% endiftruthy %}
352
- {% iffalsy child.href %}
353
- <button class="{{ child_class }}" type="button" {{ child_attributes }}>
354
- {{ child_content }}
355
- </button>
356
- {% endiffalsy %}
357
- </li>
356
+
357
+ {% if child.divider %}
358
+ <li><hr class="dropdown-divider" {{ child_attributes }}/></li>
359
+ {% else %}
360
+ <li>
361
+ {% iftruthy child.href %}
362
+ <a class="{{ child_class }}" href="{{ child.href }}" {{ child_attributes }}>
363
+ {{ child_content }}
364
+ </a>
365
+ {% endiftruthy %}
366
+ {% iffalsy child.href %}
367
+ <button class="{{ child_class }}" type="button" {{ child_attributes }}>
368
+ {{ child_content }}
369
+ </button>
370
+ {% endiffalsy %}
371
+ </li>
372
+ {% endif %}
358
373
  {% endfor %}
359
374
  </ul>
360
375
  </div>
@@ -7,15 +7,26 @@ meta:
7
7
  title: "Create Notification - Admin"
8
8
  description: "Create and send a new notification"
9
9
  breadcrumb: "Create Notification"
10
+
11
+ ### ICON PRE-RENDERING ###
12
+ prerender_icons:
13
+ - name: "mobile"
14
+ class: "fa-sm me-1"
15
+ - name: "envelope"
16
+ class: "fa-sm me-1"
17
+ - name: "comment-sms"
18
+ class: "fa-sm me-1"
19
+ - name: "bell"
20
+ class: "fa-sm me-1"
10
21
  ---
11
22
 
12
23
  <!-- Success/Error Alerts -->
13
24
  <div class="notification-success-alert alert alert-success d-none" role="alert">
14
- <i class="bi bi-check-circle"></i> <span class="alert-message">Notification sent successfully!</span>
25
+ {% uj_icon "circle-check", "fa-sm" %} <span class="alert-message">Notification sent successfully!</span>
15
26
  </div>
16
27
 
17
28
  <div class="notification-error-alert alert alert-danger d-none" role="alert">
18
- <i class="bi bi-exclamation-triangle"></i> <span class="alert-message">An error occurred</span>
29
+ {% uj_icon "triangle-exclamation", "fa-sm" %} <span class="alert-message">An error occurred</span>
19
30
  </div>
20
31
 
21
32
  <!-- Notification Creation Form -->