ultimate-jekyll-manager 1.0.3 → 1.0.4

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 (29) hide show
  1. package/CLAUDE.md +64 -1
  2. package/TODO.md +13 -0
  3. package/dist/assets/css/pages/admin/calendar/index.scss +212 -18
  4. package/dist/assets/js/pages/admin/calendar/calendar-core.js +535 -95
  5. package/dist/assets/js/pages/admin/calendar/calendar-events.js +631 -124
  6. package/dist/assets/js/pages/admin/calendar/calendar-renderer.js +238 -69
  7. package/dist/assets/js/pages/admin/calendar/campaign-preview.js +100 -0
  8. package/dist/assets/js/pages/admin/calendar/index.js +3 -16
  9. package/dist/assets/js/pages/contact/index.js +5 -1
  10. package/dist/defaults/dist/_includes/admin/sections/sidebar.json +0 -34
  11. package/dist/defaults/dist/_includes/admin/sections/topbar.json +0 -34
  12. package/dist/defaults/dist/_includes/themes/classy/backend/sections/topbar.html +1 -72
  13. package/dist/defaults/dist/_includes/themes/classy/frontend/sections/nav.html +7 -140
  14. package/dist/defaults/dist/_includes/themes/classy/global/sections/account.html +72 -0
  15. package/dist/defaults/dist/_layouts/blueprint/admin/calendar/index.html +442 -159
  16. package/dist/defaults/src/_includes/backend/sections/topbar.json +0 -34
  17. package/dist/defaults/src/_includes/frontend/sections/nav.json +0 -34
  18. package/dist/defaults/src/_includes/global/sections/account.json +36 -0
  19. package/package.json +2 -1
  20. package/dist/assets/js/pages/admin/notifications/index.js +0 -53
  21. package/dist/assets/js/pages/admin/notifications/new/index.js +0 -492
  22. package/dist/defaults/dist/_layouts/blueprint/admin/newsletters/index.html +0 -59
  23. package/dist/defaults/dist/_layouts/blueprint/admin/newsletters/new.html +0 -46
  24. package/dist/defaults/dist/_layouts/blueprint/admin/notifications/index.html +0 -103
  25. package/dist/defaults/dist/_layouts/blueprint/admin/notifications/new.html +0 -399
  26. package/dist/defaults/dist/pages/admin/newsletters/index.html +0 -7
  27. package/dist/defaults/dist/pages/admin/newsletters/new.html +0 -7
  28. package/dist/defaults/dist/pages/admin/notifications/index.html +0 -7
  29. package/dist/defaults/dist/pages/admin/notifications/new.html +0 -7
@@ -42,40 +42,6 @@
42
42
  ['data-wm-bind', '@show auth.user'],
43
43
  ['hidden', '']
44
44
  ],
45
- dropdown: [
46
- {
47
- label: 'Account',
48
- href: '/account#profile',
49
- icon: 'user-gear'
50
- },
51
- {
52
- label: 'Dashboard',
53
- href: '/dashboard',
54
- icon: 'gauge-high'
55
- },
56
- {
57
- divider: true,
58
- attributes: [
59
- ['data-wm-bind', '@show auth.account.roles.admin']
60
- ]
61
- },
62
- {
63
- label: 'Admin Panel',
64
- href: '/admin',
65
- icon: 'shield-halved',
66
- attributes: [
67
- ['data-wm-bind', '@show auth.account.roles.admin']
68
- ]
69
- },
70
- {
71
- divider: true,
72
- },
73
- {
74
- label: 'Sign Out',
75
- icon: 'arrow-right-from-bracket',
76
- class: 'auth-signout-btn text-danger'
77
- },
78
- ]
79
45
  }
80
46
  ]
81
47
  }
@@ -65,40 +65,6 @@
65
65
  ['data-wm-bind', '@show auth.user'],
66
66
  ['hidden', '']
67
67
  ],
68
- dropdown: [
69
- {
70
- label: 'Account',
71
- href: '/account#profile',
72
- icon: 'user-gear'
73
- },
74
- {
75
- label: 'Dashboard',
76
- href: '/dashboard',
77
- icon: 'gauge-high'
78
- },
79
- {
80
- divider: true,
81
- attributes: [
82
- ['data-wm-bind', '@show auth.account.roles.admin']
83
- ]
84
- },
85
- {
86
- label: 'Admin Panel',
87
- href: '/admin/dashboard',
88
- icon: 'shield-halved',
89
- attributes: [
90
- ['data-wm-bind', '@show auth.account.roles.admin']
91
- ]
92
- },
93
- {
94
- divider: true,
95
- },
96
- {
97
- label: 'Sign Out',
98
- icon: 'arrow-right-from-bracket',
99
- class: 'auth-signout-btn text-danger'
100
- }
101
- ]
102
68
  }
103
69
  ]
104
70
  }
@@ -0,0 +1,36 @@
1
+ {
2
+ dropdown: [
3
+ {
4
+ label: 'Account',
5
+ href: '/account#profile',
6
+ icon: 'user-gear'
7
+ },
8
+ {
9
+ label: 'Dashboard',
10
+ href: '/dashboard',
11
+ icon: 'gauge-high'
12
+ },
13
+ {
14
+ divider: true,
15
+ attributes: [
16
+ ['data-wm-bind', '@show auth.account.roles.admin']
17
+ ]
18
+ },
19
+ {
20
+ label: 'Admin Panel',
21
+ href: '/admin/dashboard',
22
+ icon: 'shield-halved',
23
+ attributes: [
24
+ ['data-wm-bind', '@show auth.account.roles.admin']
25
+ ]
26
+ },
27
+ {
28
+ divider: true,
29
+ },
30
+ {
31
+ label: 'Sign Out',
32
+ icon: 'arrow-right-from-bracket',
33
+ class: 'auth-signout-btn text-danger'
34
+ }
35
+ ]
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-jekyll-manager",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Ultimate Jekyll dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -95,6 +95,7 @@
95
95
  "json5": "^2.2.3",
96
96
  "libsodium-wrappers": "^0.8.2",
97
97
  "lodash": "^4.17.23",
98
+ "markdown-it": "^14.1.1",
98
99
  "minimatch": "^10.2.4",
99
100
  "node-powertools": "^3.0.0",
100
101
  "npm-api": "^1.0.1",
@@ -1,53 +0,0 @@
1
- /**
2
- * Admin Notifications Index Page JavaScript
3
- */
4
-
5
- // Libraries
6
- import { setStatValue } from '__main_assets__/js/libs/admin-helpers.js';
7
-
8
- // State
9
- let webManager = null;
10
-
11
- // Module
12
- export default (Manager) => {
13
- return new Promise(async function (resolve) {
14
- webManager = Manager.webManager;
15
-
16
- await webManager.dom().ready();
17
-
18
- webManager.auth().listen({ once: true }, async (state) => {
19
- if (!state.user) {
20
- showUnauthenticated();
21
- return;
22
- }
23
-
24
- loadStats();
25
- });
26
-
27
- return resolve();
28
- });
29
- };
30
-
31
- // Show unauthenticated state
32
- function showUnauthenticated() {
33
- document.querySelectorAll('.spinner-border').forEach((spinner) => {
34
- spinner.replaceWith(Object.assign(document.createElement('span'), {
35
- className: 'text-muted small',
36
- textContent: 'Sign in to view',
37
- }));
38
- });
39
- }
40
-
41
- // Load stat card counts
42
- async function loadStats() {
43
- const { collection, getCountFromServer } = await import('firebase/firestore');
44
- const db = webManager.firebaseFirestore;
45
-
46
- const [pushSubscribers, totalUsers] = await Promise.allSettled([
47
- getCountFromServer(collection(db, 'notifications')),
48
- getCountFromServer(collection(db, 'users')),
49
- ]);
50
-
51
- setStatValue('stat-push-subscribers', pushSubscribers);
52
- setStatValue('stat-total-users', totalUsers);
53
- }
@@ -1,492 +0,0 @@
1
- /**
2
- * Admin Notifications Page JavaScript
3
- */
4
-
5
- // Libraries
6
- import { FormManager } from '__main_assets__/js/libs/form-manager.js';
7
- import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
8
- import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';
9
-
10
- let webManager = null;
11
-
12
- // Module
13
- export default (Manager) => {
14
- return new Promise(async function (resolve) {
15
- // Shortcuts
16
- webManager = Manager.webManager;
17
-
18
- // Initialize when DOM is ready
19
- await webManager.dom().ready();
20
-
21
- try {
22
- await initializeNotificationCreator();
23
- } catch (error) {
24
- webManager.sentry().captureException(new Error('Failed to initialize notification creator', { cause: error }));
25
- }
26
-
27
- // Resolve after initialization
28
- return resolve();
29
- });
30
- };
31
-
32
- // Global state
33
- let formManager = null;
34
- let stats = {
35
- totalUsers: 0,
36
- filteredUsers: 0,
37
- };
38
- let autoSubmitTimer = null;
39
- let isInitialized = false;
40
-
41
- // Main initialization
42
- async function initializeNotificationCreator() {
43
- // Prevent re-initialization
44
- if (isInitialized) {
45
- return;
46
- }
47
- isInitialized = true;
48
-
49
- // Initialize FormManager
50
- initializeFormManager();
51
-
52
- // Initialize UI elements
53
- initializeUI();
54
-
55
- // Show loading spinner on user counts initially
56
- showUserCountLoading();
57
-
58
- // Setup auth listener
59
- webManager.auth().listen({}, async (state) => {
60
- console.log('Auth state for notification creator:', state);
61
-
62
- // Show loading spinner while fetching
63
- showUserCountLoading();
64
-
65
- // Fetch initial user stats
66
- await fetchUserStats();
67
-
68
- // Update UI with stats
69
- updateUserCount();
70
- });
71
- }
72
-
73
- // Initialize FormManager
74
- function initializeFormManager() {
75
- formManager = new FormManager('#notification-form', {
76
- allowResubmit: false,
77
- });
78
-
79
- formManager.on('change', ({ data }) => {
80
- // Update preview in real-time
81
- updatePreview(data);
82
-
83
- // Update character counts
84
- updateCharacterCounts(data);
85
-
86
- // Check auto-submit status
87
- checkAutoSubmit(data);
88
- });
89
-
90
- formManager.on('submit', async ({ data }) => {
91
- // Transform data for API
92
- const payload = transformDataForAPI(data);
93
-
94
- // Add user stats
95
- payload.reach = {
96
- available: { total: stats.totalUsers },
97
- filtered: { total: stats.filteredUsers },
98
- };
99
-
100
- // Call API
101
- await sendNotification(payload);
102
-
103
- // Track success
104
- trackNotificationSent(payload);
105
-
106
- // Success
107
- formManager.showSuccess(`Notification sent successfully to ${stats.filteredUsers.toLocaleString()} users!`);
108
- });
109
- }
110
-
111
- // Initialize UI elements
112
- function initializeUI() {
113
- // Icon preview updater
114
- const $iconInput = document.querySelector('input[name="notification.icon"]');
115
- if ($iconInput) {
116
- $iconInput.addEventListener('input', (e) => {
117
- updateIconPreview(e.target.value);
118
- });
119
- }
120
-
121
- // Title character counter
122
- const $titleInput = document.querySelector('input[name="notification.title"]');
123
- if ($titleInput) {
124
- // Set default value in dev mode
125
- if (webManager.isDevelopment()) {
126
- $titleInput.value = 'Test Notification Title';
127
- }
128
-
129
- $titleInput.addEventListener('input', (e) => {
130
- const length = e.target.value.length;
131
- const $titleCount = document.querySelector('#title-char-count');
132
- if ($titleCount) {
133
- $titleCount.textContent = `${length} / 60`;
134
- $titleCount.className = length > 60 ? 'text-danger' : 'text-muted';
135
- }
136
- });
137
- }
138
-
139
- // Body character counter
140
- const $bodyInput = document.querySelector('textarea[name="notification.body"]');
141
- if ($bodyInput) {
142
- // Set default value in dev mode
143
- if (webManager.isDevelopment()) {
144
- $bodyInput.value = 'This is a test notification body message for development.';
145
- }
146
-
147
- $bodyInput.addEventListener('input', (e) => {
148
- const length = e.target.value.length;
149
- const $bodyCount = document.querySelector('#body-char-count');
150
- if ($bodyCount) {
151
- $bodyCount.textContent = `${length} / 200`;
152
- $bodyCount.className = length > 200 ? 'text-danger' : 'text-muted';
153
- }
154
- });
155
- }
156
-
157
- // Auto-submit button
158
- const $autoSubmitBtn = document.querySelector('.btn-set-auto-submit');
159
- if ($autoSubmitBtn) {
160
- $autoSubmitBtn.addEventListener('click', () => {
161
- setAutoSubmitTime();
162
- });
163
- }
164
-
165
- // Clear auto-submit button
166
- const $clearAutoSubmitBtn = document.querySelector('.btn-clear-auto-submit');
167
- if ($clearAutoSubmitBtn) {
168
- $clearAutoSubmitBtn.addEventListener('click', () => {
169
- clearAutoSubmit();
170
- });
171
- }
172
-
173
- // Notification preview click handler
174
- const $notificationPreview = document.querySelector('#notification-preview-clickable');
175
- if ($notificationPreview) {
176
- $notificationPreview.addEventListener('click', () => {
177
- const $clickActionInput = document.querySelector('input[name="notification.clickAction"]');
178
- if ($clickActionInput && $clickActionInput.value) {
179
- // Open the URL in a new tab
180
- window.open($clickActionInput.value, '_blank', 'noopener,noreferrer');
181
- }
182
- });
183
- }
184
-
185
- // Initialize preview with default values and trigger form change to update everything
186
- updatePreview();
187
-
188
- // Trigger initial form change to update character counts and preview with form data
189
- if (formManager) {
190
- setTimeout(() => {
191
- const data = formManager.getData();
192
- updatePreview(data);
193
- updateCharacterCounts(data);
194
- checkAutoSubmit(data);
195
- }, 100);
196
- }
197
- }
198
-
199
- // Transform data for API
200
- function transformDataForAPI(formData) {
201
- console.log('[Debug] transformDataForAPI called');
202
- console.log('[Debug] formData received:', formData);
203
- console.log('[Debug] formData.notification:', formData.notification);
204
-
205
- const now = new Date();
206
- const notification = formData.notification || {};
207
-
208
- console.log('[Debug] notification object:', notification);
209
- console.log('[Debug] notification.clickAction:', notification.clickAction);
210
- console.log('[Debug] typeof notification:', typeof notification);
211
- console.log('[Debug] Object.keys(notification):', Object.keys(notification));
212
-
213
- // Generate ID
214
- const id = now.getTime();
215
-
216
- // Build click action URL with tracking
217
- const redirectUrl = new URL('https://promo-server.itwcreativeworks.com/redirect/notification');
218
- redirectUrl.searchParams.set('id', id);
219
- redirectUrl.searchParams.set('type', 'notification');
220
-
221
- // Make sure we have the actual clickAction value
222
- const clickActionValue = notification.clickAction || '';
223
- console.log('[Debug] clickActionValue to be set in URL:', clickActionValue);
224
- redirectUrl.searchParams.set('url', clickActionValue);
225
-
226
- return {
227
- id: id,
228
- notification: {
229
- icon: notification.icon || '',
230
- title: notification.title || '',
231
- body: notification.body || '',
232
- clickAction: redirectUrl.toString(),
233
- },
234
- created: now.toISOString(),
235
- channels: formData.channels || {},
236
- audience: formData.audience || {},
237
- schedule: formData.schedule || {},
238
- };
239
- }
240
-
241
- // Send notification via API
242
- async function sendNotification(payload) {
243
- const functionsUrl = `${getAPIFunctionsUrl()}/admin/notification`;
244
-
245
- return authorizedFetch(functionsUrl, {
246
- method: 'POST',
247
- timeout: 60000,
248
- response: 'json',
249
- tries: 1,
250
- log: true,
251
- body: payload,
252
- });
253
- }
254
-
255
- // Fetch user statistics
256
- async function fetchUserStats() {
257
- try {
258
- const response = await authorizedFetch(`${getAPIFunctionsUrl()}/admin/firestore?path=meta/stats`, {
259
- method: 'GET',
260
- timeout: 60000,
261
- response: 'json',
262
- tries: 2,
263
- log: true,
264
- });
265
-
266
- // Extract user count from response
267
- const total = response?.notifications?.total || 0;
268
- stats.totalUsers = total;
269
- stats.filteredUsers = total; // Initially all users
270
- } catch (error) {
271
- console.error('Failed to fetch user stats:', error);
272
- webManager.sentry().captureException(new Error('Failed to fetch user stats', { cause: error }));
273
-
274
- // Use default values
275
- stats.totalUsers = 0;
276
- stats.filteredUsers = 0;
277
- }
278
- }
279
-
280
- // Update user count display
281
- function updateUserCount() {
282
- const $countElements = document.querySelectorAll('.notification-user-count');
283
- $countElements.forEach(el => {
284
- // Remove loading spinner and show the count
285
- el.innerHTML = `${stats.filteredUsers.toLocaleString()} / ${stats.totalUsers.toLocaleString()}`;
286
- });
287
- }
288
-
289
- // Show loading spinner on user count elements
290
- function showUserCountLoading() {
291
- const $countElements = document.querySelectorAll('.notification-user-count');
292
- $countElements.forEach(el => {
293
- // Add spinner with loading text
294
- el.innerHTML = '<span class="spinner-border spinner-border-sm me-1" role="status"></span>Loading...';
295
- });
296
- }
297
-
298
- // Update preview
299
- function updatePreview(formData = null) {
300
- if (!formData) {
301
- formData = formManager?.getData() || {};
302
- }
303
-
304
- const notification = formData.notification || {};
305
-
306
- // Update preview elements
307
- const $previewIcon = document.querySelector('#preview-icon');
308
- const $previewTitle = document.querySelector('#preview-title');
309
- const $previewBody = document.querySelector('#preview-body');
310
- const $previewTime = document.querySelector('#preview-time');
311
-
312
- if ($previewIcon && notification.icon) {
313
- $previewIcon.src = notification.icon;
314
- $previewIcon.onerror = () => {
315
- $previewIcon.src = 'https://via.placeholder.com/50';
316
- };
317
- }
318
-
319
- if ($previewTitle) {
320
- $previewTitle.textContent = notification.title || 'Notification Title';
321
- }
322
-
323
- if ($previewBody) {
324
- $previewBody.textContent = notification.body || 'This is what your notification body will look like...';
325
- }
326
-
327
- if ($previewTime) {
328
- $previewTime.textContent = 'Now';
329
- }
330
- }
331
-
332
- // Update icon preview
333
- function updateIconPreview(url) {
334
- const $previewIcon = document.querySelector('#preview-icon');
335
- if ($previewIcon) {
336
- if (url && url.match(/^https?:\/\/.+/)) {
337
- $previewIcon.src = url;
338
- $previewIcon.onerror = () => {
339
- $previewIcon.src = 'https://via.placeholder.com/50';
340
- };
341
- } else {
342
- $previewIcon.src = 'https://via.placeholder.com/50';
343
- }
344
- }
345
- }
346
-
347
- // Update character counts
348
- function updateCharacterCounts(formData) {
349
- const notification = formData.notification || {};
350
-
351
- // Update title count
352
- const $titleCount = document.querySelector('#title-char-count');
353
- if ($titleCount) {
354
- const length = (notification.title || '').length;
355
- $titleCount.textContent = `${length} / 60`;
356
- $titleCount.className = length > 60 ? 'text-danger' : 'text-muted';
357
- }
358
-
359
- // Update body count
360
- const $bodyCount = document.querySelector('#body-char-count');
361
- if ($bodyCount) {
362
- const length = (notification.body || '').length;
363
- $bodyCount.textContent = `${length} / 200`;
364
- $bodyCount.className = length > 200 ? 'text-danger' : 'text-muted';
365
- }
366
- }
367
-
368
- // Set auto-submit time
369
- function setAutoSubmitTime() {
370
- // Set to tomorrow at 10 AM
371
- const tomorrow = new Date();
372
- tomorrow.setDate(tomorrow.getDate() + 1);
373
- tomorrow.setHours(10, 0, 0, 0);
374
-
375
- // Format for input fields
376
- const dateStr = tomorrow.toISOString().split('T')[0];
377
- const timeStr = '10:00';
378
-
379
- // Set values
380
- const $dateInput = document.querySelector('input[name="schedule.date"]');
381
- const $timeInput = document.querySelector('input[name="schedule.time"]');
382
-
383
- if ($dateInput) $dateInput.value = dateStr;
384
- if ($timeInput) $timeInput.value = timeStr;
385
-
386
- // Start countdown
387
- startAutoSubmitCountdown(tomorrow);
388
- }
389
-
390
- // Clear auto-submit
391
- function clearAutoSubmit() {
392
- // Clear inputs
393
- const $dateInput = document.querySelector('input[name="schedule.date"]');
394
- const $timeInput = document.querySelector('input[name="schedule.time"]');
395
-
396
- if ($dateInput) $dateInput.value = '';
397
- if ($timeInput) $timeInput.value = '';
398
-
399
- // Clear countdown
400
- clearAutoSubmitCountdown();
401
- }
402
-
403
- // Check auto-submit status
404
- function checkAutoSubmit(formData) {
405
- if (formData.schedule?.date && formData.schedule?.time) {
406
- const scheduledDate = new Date(`${formData.schedule.date}T${formData.schedule.time}`);
407
- if (scheduledDate > new Date()) {
408
- startAutoSubmitCountdown(scheduledDate);
409
- } else {
410
- clearAutoSubmitCountdown();
411
- }
412
- } else {
413
- clearAutoSubmitCountdown();
414
- }
415
- }
416
-
417
- // Start auto-submit countdown
418
- function startAutoSubmitCountdown(targetDate) {
419
- clearAutoSubmitCountdown();
420
-
421
- const $countdownEl = document.querySelector('#auto-submit-countdown');
422
- if (!$countdownEl) {
423
- return;
424
- }
425
-
426
- $countdownEl.classList.remove('d-none');
427
-
428
- autoSubmitTimer = setInterval(() => {
429
- const now = new Date();
430
- const diff = targetDate - now;
431
-
432
- if (diff <= 0) {
433
- // Time to submit!
434
- clearAutoSubmitCountdown();
435
-
436
- // Fetch updated stats before submitting
437
- fetchUserStats()
438
- .then(() => updateUserCount())
439
- .finally(() => {
440
- // Programmatically submit the form
441
- formManager.$form.requestSubmit();
442
- });
443
-
444
- return;
445
- }
446
-
447
- const hours = Math.floor(diff / (1000 * 60 * 60));
448
- const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
449
- const seconds = Math.floor((diff % (1000 * 60)) / 1000);
450
-
451
- $countdownEl.innerHTML = `<i class="bi bi-clock"></i> Auto-submit in: ${hours}h ${minutes}m ${seconds}s`;
452
- }, 1000);
453
- }
454
-
455
- // Clear auto-submit countdown
456
- function clearAutoSubmitCountdown() {
457
- if (autoSubmitTimer) {
458
- clearInterval(autoSubmitTimer);
459
- autoSubmitTimer = null;
460
- }
461
-
462
- const $countdownEl = document.querySelector('#auto-submit-countdown');
463
- if ($countdownEl) {
464
- $countdownEl.classList.add('d-none');
465
- $countdownEl.innerHTML = '';
466
- }
467
- }
468
-
469
- // Get API functions URL
470
- function getAPIFunctionsUrl() {
471
- return `${webManager.getApiUrl()}/backend-manager`;
472
- }
473
-
474
- // Tracking functions
475
- function trackNotificationSent(payload) {
476
- gtag('event', 'notification_sent', {
477
- notification_type: 'admin',
478
- user_count: stats.filteredUsers,
479
- channels: Object.keys(payload.channels || {}).filter(c => payload.channels[c]?.enabled).join(','),
480
- });
481
-
482
- fbq('trackCustom', 'AdminNotificationSent', {
483
- users: stats.filteredUsers,
484
- channels: Object.keys(payload.channels || {}).filter(c => payload.channels[c]?.enabled),
485
- });
486
-
487
- ttq.track('SubmitForm', {
488
- content_id: 'admin-notification',
489
- content_type: 'product',
490
- content_name: 'Admin Notification',
491
- });
492
- }