ultimate-jekyll-manager 0.0.144 → 0.0.146
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/.playwright-mcp/page-2025-12-09T08-57-21-148Z.png +0 -0
- package/.playwright-mcp/page-2025-12-09T09-11-17-887Z.png +0 -0
- package/.playwright-mcp/page-2025-12-09T09-11-41-348Z.png +0 -0
- package/dist/assets/css/pages/extension/installed/index.scss +240 -0
- package/dist/assets/css/pages/status/index.scss +174 -0
- package/dist/assets/css/pages/updates/index.scss +13 -0
- package/dist/assets/css/pages/updates/update.scss +46 -0
- package/dist/assets/js/pages/extension/index.js +160 -0
- package/dist/assets/js/pages/status/index.js +508 -0
- package/dist/assets/js/pages/updates/index.js +30 -0
- package/dist/assets/js/pages/updates/update.js +34 -0
- package/dist/defaults/dist/_includes/core/head.html +9 -0
- package/dist/defaults/dist/_layouts/blueprint/extension/index.html +12 -0
- package/dist/defaults/dist/_layouts/blueprint/extension/installed.html +13 -0
- package/dist/defaults/dist/_layouts/blueprint/status.html +12 -0
- package/dist/defaults/dist/_layouts/blueprint/updates/index.html +12 -0
- package/dist/defaults/dist/_layouts/blueprint/updates/update.html +15 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/extension/index.html +434 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/extension/installed.html +188 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/payment/confirmation.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/status.html +296 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/updates/index.html +128 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/updates/update.html +122 -0
- package/dist/defaults/dist/_updates/v0.0.0.md +32 -0
- package/dist/defaults/dist/_updates/v0.0.1.md +64 -0
- package/dist/defaults/dist/pages/{extension.md → extension/index.html} +2 -2
- package/dist/defaults/dist/pages/extension/installed.html +7 -0
- package/dist/defaults/dist/pages/status.md +7 -0
- package/dist/defaults/dist/pages/updates/index.md +7 -0
- package/dist/defaults/dist/sitemap.html +8 -0
- package/dist/defaults/dist/sitemap.xml +4 -0
- package/dist/defaults/src/_config.yml +8 -0
- package/dist/gulp/main.js +4 -0
- package/dist/gulp/tasks/serve.js +16 -36
- package/firebase-debug.log +98 -0
- package/package.json +2 -1
- package/dist/defaults/dist/_layouts/blueprint/extension.md +0 -47
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status 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 components
|
|
20
|
+
initializeUptimeBars();
|
|
21
|
+
initializeSubscribeForm();
|
|
22
|
+
initializeTooltips();
|
|
23
|
+
initializeRefreshTimer();
|
|
24
|
+
|
|
25
|
+
// Fetch status data (if configured)
|
|
26
|
+
fetchStatusData();
|
|
27
|
+
|
|
28
|
+
// Resolve after initialization
|
|
29
|
+
return resolve();
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Configuration
|
|
34
|
+
const config = {
|
|
35
|
+
selectors: {
|
|
36
|
+
statusBanner: '#status-banner',
|
|
37
|
+
statusText: '#status-text',
|
|
38
|
+
servicesList: '#services-list',
|
|
39
|
+
maintenanceList: '#maintenance-list',
|
|
40
|
+
maintenanceEmpty: '#maintenance-empty',
|
|
41
|
+
incidentsList: '#incidents-list',
|
|
42
|
+
incidentsEmpty: '#incidents-empty',
|
|
43
|
+
subscribeForm: '#status-subscribe-form',
|
|
44
|
+
},
|
|
45
|
+
statusClasses: {
|
|
46
|
+
operational: 'status-operational',
|
|
47
|
+
degraded: 'status-degraded',
|
|
48
|
+
major: 'status-major',
|
|
49
|
+
maintenance: 'status-maintenance',
|
|
50
|
+
},
|
|
51
|
+
statusLabels: {
|
|
52
|
+
operational: 'Operational',
|
|
53
|
+
degraded: 'Degraded Performance',
|
|
54
|
+
major: 'Major Outage',
|
|
55
|
+
maintenance: 'Under Maintenance',
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Initialize uptime bar tooltips
|
|
60
|
+
function initializeUptimeBars() {
|
|
61
|
+
const $uptimeBars = document.querySelectorAll('.uptime-bars');
|
|
62
|
+
|
|
63
|
+
$uptimeBars.forEach($barsContainer => {
|
|
64
|
+
const $bars = $barsContainer.querySelectorAll('.uptime-bar');
|
|
65
|
+
|
|
66
|
+
$bars.forEach(($bar, index) => {
|
|
67
|
+
$bar.addEventListener('mouseenter', (e) => showUptimeTooltip(e, $bar, index, $bars.length));
|
|
68
|
+
$bar.addEventListener('mouseleave', hideUptimeTooltip);
|
|
69
|
+
$bar.addEventListener('mousemove', moveUptimeTooltip);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Show uptime tooltip
|
|
75
|
+
function showUptimeTooltip(e, $bar, index, totalDays) {
|
|
76
|
+
// Get the actual day number from data attribute (1-90)
|
|
77
|
+
const dayNumber = parseInt($bar.dataset.day, 10) || (index + 1);
|
|
78
|
+
|
|
79
|
+
// Calculate days ago (day 1 = 89 days ago, day 90 = today)
|
|
80
|
+
const daysAgo = totalDays - dayNumber;
|
|
81
|
+
const date = new Date();
|
|
82
|
+
date.setDate(date.getDate() - daysAgo);
|
|
83
|
+
|
|
84
|
+
// Get status from the bar's class
|
|
85
|
+
let status = 'operational';
|
|
86
|
+
if ($bar.classList.contains('status-degraded')) {
|
|
87
|
+
status = 'degraded';
|
|
88
|
+
} else if ($bar.classList.contains('status-major')) {
|
|
89
|
+
status = 'major';
|
|
90
|
+
} else if ($bar.classList.contains('status-maintenance')) {
|
|
91
|
+
status = 'maintenance';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get uptime data if available
|
|
95
|
+
const uptime = $bar.dataset.uptime || '100%';
|
|
96
|
+
|
|
97
|
+
// Create tooltip
|
|
98
|
+
let $tooltip = document.querySelector('.uptime-tooltip');
|
|
99
|
+
if (!$tooltip) {
|
|
100
|
+
$tooltip = document.createElement('div');
|
|
101
|
+
$tooltip.className = 'uptime-tooltip rounded-3 p-2 small';
|
|
102
|
+
document.body.appendChild($tooltip);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Format date
|
|
106
|
+
const dateStr = date.toLocaleDateString('en-US', {
|
|
107
|
+
weekday: 'short',
|
|
108
|
+
month: 'short',
|
|
109
|
+
day: 'numeric',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
$tooltip.innerHTML = `
|
|
113
|
+
<div class="fw-semibold mb-1">${dateStr}</div>
|
|
114
|
+
<div class="d-flex align-items-center gap-2">
|
|
115
|
+
<span class="status-dot rounded-circle ${config.statusClasses[status]}"></span>
|
|
116
|
+
<span>${config.statusLabels[status]}</span>
|
|
117
|
+
</div>
|
|
118
|
+
<div class="text-muted small">Uptime: ${uptime}</div>
|
|
119
|
+
`;
|
|
120
|
+
|
|
121
|
+
$tooltip.style.display = 'block';
|
|
122
|
+
positionTooltip(e, $tooltip);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Hide uptime tooltip
|
|
126
|
+
function hideUptimeTooltip() {
|
|
127
|
+
const $tooltip = document.querySelector('.uptime-tooltip');
|
|
128
|
+
if ($tooltip) {
|
|
129
|
+
$tooltip.style.display = 'none';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Move uptime tooltip with cursor
|
|
134
|
+
function moveUptimeTooltip(e) {
|
|
135
|
+
const $tooltip = document.querySelector('.uptime-tooltip');
|
|
136
|
+
if ($tooltip) {
|
|
137
|
+
positionTooltip(e, $tooltip);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Position tooltip near cursor
|
|
142
|
+
function positionTooltip(e, $tooltip) {
|
|
143
|
+
const padding = 10;
|
|
144
|
+
const tooltipRect = $tooltip.getBoundingClientRect();
|
|
145
|
+
|
|
146
|
+
let left = e.clientX + padding;
|
|
147
|
+
let top = e.clientY - tooltipRect.height - padding;
|
|
148
|
+
|
|
149
|
+
// Keep tooltip within viewport
|
|
150
|
+
if (left + tooltipRect.width > window.innerWidth) {
|
|
151
|
+
left = e.clientX - tooltipRect.width - padding;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (top < 0) {
|
|
155
|
+
top = e.clientY + padding;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
$tooltip.style.left = `${left}px`;
|
|
159
|
+
$tooltip.style.top = `${top}px`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Initialize subscribe form
|
|
163
|
+
function initializeSubscribeForm() {
|
|
164
|
+
const $form = document.querySelector(config.selectors.subscribeForm);
|
|
165
|
+
|
|
166
|
+
if (!$form) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const formManager = new FormManager($form, {
|
|
171
|
+
submittedText: 'Subscribed!',
|
|
172
|
+
allowResubmit: false,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
formManager.on('submit', async () => {
|
|
176
|
+
// Track subscription
|
|
177
|
+
trackStatusSubscribe();
|
|
178
|
+
|
|
179
|
+
// Here you would typically send to your backend
|
|
180
|
+
// For now, we'll simulate a successful subscription
|
|
181
|
+
await simulateApiCall();
|
|
182
|
+
|
|
183
|
+
formManager.showSuccess('You\'ve been subscribed to status updates!');
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Initialize Bootstrap tooltips
|
|
188
|
+
function initializeTooltips() {
|
|
189
|
+
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
|
190
|
+
tooltipTriggerList.forEach(tooltipTriggerEl => {
|
|
191
|
+
new bootstrap.Tooltip(tooltipTriggerEl);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Initialize refresh timer - counts up every second, resets every 60 seconds
|
|
196
|
+
function initializeRefreshTimer() {
|
|
197
|
+
const $timer = document.querySelector('#status-refresh-timer');
|
|
198
|
+
|
|
199
|
+
if (!$timer) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let seconds = 0;
|
|
204
|
+
|
|
205
|
+
setInterval(() => {
|
|
206
|
+
seconds++;
|
|
207
|
+
|
|
208
|
+
// Reset and refresh data every 60 seconds
|
|
209
|
+
if (seconds >= 60) {
|
|
210
|
+
seconds = 0;
|
|
211
|
+
fetchStatusData();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
$timer.textContent = seconds;
|
|
215
|
+
}, 1000);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Fetch status data from API (if configured)
|
|
219
|
+
function fetchStatusData() {
|
|
220
|
+
// Check if there's a status API endpoint configured
|
|
221
|
+
const statusApiUrl = window.statusConfig?.apiUrl;
|
|
222
|
+
|
|
223
|
+
if (!statusApiUrl) {
|
|
224
|
+
// No API configured, use static data
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Fetch from API
|
|
229
|
+
fetch(statusApiUrl)
|
|
230
|
+
.then(response => response.json())
|
|
231
|
+
.then(data => {
|
|
232
|
+
updateStatusDisplay(data);
|
|
233
|
+
})
|
|
234
|
+
.catch(error => {
|
|
235
|
+
console.warn('Failed to fetch status data:', error);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Update status display with fetched data
|
|
240
|
+
function updateStatusDisplay(data) {
|
|
241
|
+
// Update overall status
|
|
242
|
+
if (data.overall) {
|
|
243
|
+
updateOverallStatus(data.overall);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Update individual services
|
|
247
|
+
if (data.services) {
|
|
248
|
+
updateServices(data.services);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Update maintenance items
|
|
252
|
+
if (data.maintenance) {
|
|
253
|
+
updateMaintenance(data.maintenance);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Update incidents
|
|
257
|
+
if (data.incidents) {
|
|
258
|
+
updateIncidents(data.incidents);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Update overall status banner
|
|
263
|
+
function updateOverallStatus(status) {
|
|
264
|
+
const $banner = document.querySelector(config.selectors.statusBanner);
|
|
265
|
+
const $text = document.querySelector(config.selectors.statusText);
|
|
266
|
+
|
|
267
|
+
if (!$banner || !$text) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Remove all status classes
|
|
272
|
+
Object.values(config.statusClasses).forEach(cls => {
|
|
273
|
+
$banner.classList.remove(cls);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Add new status class
|
|
277
|
+
$banner.classList.add(config.statusClasses[status.level] || config.statusClasses.operational);
|
|
278
|
+
|
|
279
|
+
// Update text
|
|
280
|
+
$text.textContent = status.message || config.statusLabels[status.level];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Update individual service statuses
|
|
284
|
+
function updateServices(services) {
|
|
285
|
+
services.forEach(service => {
|
|
286
|
+
const $serviceItem = document.querySelector(`[data-service-id="${service.id}"]`);
|
|
287
|
+
|
|
288
|
+
if (!$serviceItem) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Update status dot
|
|
293
|
+
const $statusDot = $serviceItem.querySelector('[data-service-status]');
|
|
294
|
+
if ($statusDot) {
|
|
295
|
+
Object.values(config.statusClasses).forEach(cls => {
|
|
296
|
+
$statusDot.classList.remove(cls);
|
|
297
|
+
});
|
|
298
|
+
$statusDot.classList.add(config.statusClasses[service.status] || config.statusClasses.operational);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Update uptime percentage
|
|
302
|
+
const $uptime = $serviceItem.querySelector('[data-service-uptime]');
|
|
303
|
+
if ($uptime && service.uptime !== undefined) {
|
|
304
|
+
$uptime.textContent = `${service.uptime}%`;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Update uptime bars if provided
|
|
308
|
+
if (service.history) {
|
|
309
|
+
updateUptimeBars($serviceItem, service.history);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Update uptime bars for a service
|
|
315
|
+
function updateUptimeBars($serviceItem, history) {
|
|
316
|
+
const $bars = $serviceItem.querySelectorAll('.uptime-bar');
|
|
317
|
+
|
|
318
|
+
history.forEach((day, index) => {
|
|
319
|
+
const $bar = $bars[index];
|
|
320
|
+
|
|
321
|
+
if (!$bar) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Remove all status classes
|
|
326
|
+
Object.values(config.statusClasses).forEach(cls => {
|
|
327
|
+
$bar.classList.remove(cls);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Add new status class
|
|
331
|
+
$bar.classList.add(config.statusClasses[day.status] || config.statusClasses.operational);
|
|
332
|
+
|
|
333
|
+
// Store uptime data
|
|
334
|
+
if (day.uptime !== undefined) {
|
|
335
|
+
$bar.dataset.uptime = `${day.uptime}%`;
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Update maintenance section
|
|
341
|
+
function updateMaintenance(maintenanceItems) {
|
|
342
|
+
const $list = document.querySelector(config.selectors.maintenanceList);
|
|
343
|
+
const $empty = document.querySelector(config.selectors.maintenanceEmpty);
|
|
344
|
+
|
|
345
|
+
if (!$list) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (!maintenanceItems || maintenanceItems.length === 0) {
|
|
350
|
+
if ($empty) {
|
|
351
|
+
$empty.style.display = 'block';
|
|
352
|
+
}
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Hide empty state
|
|
357
|
+
if ($empty) {
|
|
358
|
+
$empty.style.display = 'none';
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Build maintenance cards
|
|
362
|
+
const html = maintenanceItems.map(item => `
|
|
363
|
+
<div class="card maintenance-card border-0 bg-body-tertiary mb-3 status-maintenance">
|
|
364
|
+
<div class="card-body p-4">
|
|
365
|
+
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
366
|
+
<h4 class="h5 fw-semibold mb-0">${escapeHtml(item.title)}</h4>
|
|
367
|
+
<span class="text-muted small">${formatDate(item.scheduled_for)}</span>
|
|
368
|
+
</div>
|
|
369
|
+
<p class="text-muted mb-0">${escapeHtml(item.description)}</p>
|
|
370
|
+
${item.affected_services ? `
|
|
371
|
+
<div class="mt-2">
|
|
372
|
+
<small class="text-muted">
|
|
373
|
+
<strong>Affected:</strong> ${item.affected_services.map(s => escapeHtml(s)).join(', ')}
|
|
374
|
+
</small>
|
|
375
|
+
</div>
|
|
376
|
+
` : ''}
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
`).join('');
|
|
380
|
+
|
|
381
|
+
// Insert before empty state
|
|
382
|
+
if ($empty) {
|
|
383
|
+
$empty.insertAdjacentHTML('beforebegin', html);
|
|
384
|
+
} else {
|
|
385
|
+
$list.innerHTML = html;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Update incidents section
|
|
390
|
+
function updateIncidents(incidents) {
|
|
391
|
+
const $list = document.querySelector(config.selectors.incidentsList);
|
|
392
|
+
const $empty = document.querySelector(config.selectors.incidentsEmpty);
|
|
393
|
+
|
|
394
|
+
if (!$list) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (!incidents || incidents.length === 0) {
|
|
399
|
+
if ($empty) {
|
|
400
|
+
$empty.style.display = 'block';
|
|
401
|
+
}
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Hide empty state
|
|
406
|
+
if ($empty) {
|
|
407
|
+
$empty.style.display = 'none';
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Build incident cards
|
|
411
|
+
const html = incidents.map(incident => `
|
|
412
|
+
<div class="card incident-card border-0 bg-body-tertiary mb-3 status-${incident.status}">
|
|
413
|
+
<div class="card-body p-4">
|
|
414
|
+
<div class="d-flex justify-content-between align-items-start mb-2 flex-wrap gap-2">
|
|
415
|
+
<h4 class="h5 fw-semibold mb-0">${escapeHtml(incident.title)}</h4>
|
|
416
|
+
<div class="d-flex align-items-center gap-2">
|
|
417
|
+
<span class="badge ${getIncidentBadgeClasses(incident.status)}">${formatIncidentStatus(incident.status)}</span>
|
|
418
|
+
<span class="text-muted small">${formatDate(incident.created_at)}</span>
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
${incident.updates && incident.updates.length > 0 ? `
|
|
422
|
+
<div class="incident-timeline mt-3">
|
|
423
|
+
${incident.updates.map(update => `
|
|
424
|
+
<div class="timeline-item status-${update.status} pb-3">
|
|
425
|
+
<div class="small text-muted mb-1">${formatDateTime(update.created_at)}</div>
|
|
426
|
+
<div class="small">${escapeHtml(update.message)}</div>
|
|
427
|
+
</div>
|
|
428
|
+
`).join('')}
|
|
429
|
+
</div>
|
|
430
|
+
` : ''}
|
|
431
|
+
</div>
|
|
432
|
+
</div>
|
|
433
|
+
`).join('');
|
|
434
|
+
|
|
435
|
+
// Insert before empty state
|
|
436
|
+
if ($empty) {
|
|
437
|
+
$empty.insertAdjacentHTML('beforebegin', html);
|
|
438
|
+
} else {
|
|
439
|
+
$list.innerHTML = html;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Tracking functions
|
|
444
|
+
function trackStatusSubscribe() {
|
|
445
|
+
gtag('event', 'status_subscribe', {
|
|
446
|
+
method: 'email'
|
|
447
|
+
});
|
|
448
|
+
fbq('track', 'Lead', {
|
|
449
|
+
content_name: 'Status Updates',
|
|
450
|
+
content_category: 'subscription'
|
|
451
|
+
});
|
|
452
|
+
ttq.track('SubmitForm', {
|
|
453
|
+
content_id: 'status-subscribe',
|
|
454
|
+
content_type: 'product',
|
|
455
|
+
content_name: 'Status Subscription'
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Helper functions
|
|
460
|
+
function escapeHtml(text) {
|
|
461
|
+
const div = document.createElement('div');
|
|
462
|
+
div.textContent = text;
|
|
463
|
+
return div.innerHTML;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function formatDate(dateStr) {
|
|
467
|
+
const date = new Date(dateStr);
|
|
468
|
+
return date.toLocaleDateString('en-US', {
|
|
469
|
+
month: 'short',
|
|
470
|
+
day: 'numeric',
|
|
471
|
+
year: 'numeric'
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function formatDateTime(dateStr) {
|
|
476
|
+
const date = new Date(dateStr);
|
|
477
|
+
return date.toLocaleString('en-US', {
|
|
478
|
+
month: 'short',
|
|
479
|
+
day: 'numeric',
|
|
480
|
+
hour: 'numeric',
|
|
481
|
+
minute: '2-digit',
|
|
482
|
+
hour12: true
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function formatIncidentStatus(status) {
|
|
487
|
+
const statusLabels = {
|
|
488
|
+
investigating: 'Investigating',
|
|
489
|
+
identified: 'Identified',
|
|
490
|
+
monitoring: 'Monitoring',
|
|
491
|
+
resolved: 'Resolved'
|
|
492
|
+
};
|
|
493
|
+
return statusLabels[status] || status;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function getIncidentBadgeClasses(status) {
|
|
497
|
+
const badgeClasses = {
|
|
498
|
+
investigating: 'bg-warning-subtle text-warning',
|
|
499
|
+
identified: 'bg-danger-subtle text-danger',
|
|
500
|
+
monitoring: 'bg-info-subtle text-info',
|
|
501
|
+
resolved: 'bg-success-subtle text-success'
|
|
502
|
+
};
|
|
503
|
+
return badgeClasses[status] || 'bg-secondary-subtle text-secondary';
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function simulateApiCall() {
|
|
507
|
+
return new Promise(resolve => setTimeout(resolve, 1000));
|
|
508
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Updates Listing Page JavaScript
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
let webManager = null;
|
|
6
|
+
|
|
7
|
+
// Module
|
|
8
|
+
export default (Manager) => {
|
|
9
|
+
return new Promise(async function (resolve) {
|
|
10
|
+
// Shortcuts
|
|
11
|
+
webManager = Manager.webManager;
|
|
12
|
+
|
|
13
|
+
// Initialize when DOM is ready
|
|
14
|
+
await webManager.dom().ready();
|
|
15
|
+
|
|
16
|
+
// Track page view
|
|
17
|
+
trackUpdatesPageView();
|
|
18
|
+
|
|
19
|
+
// Resolve after initialization
|
|
20
|
+
return resolve();
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Tracking functions
|
|
25
|
+
function trackUpdatesPageView() {
|
|
26
|
+
gtag('event', 'page_view', {
|
|
27
|
+
page_title: 'Updates',
|
|
28
|
+
content_group: 'updates'
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Individual Update Page JavaScript
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
let webManager = null;
|
|
6
|
+
|
|
7
|
+
// Module
|
|
8
|
+
export default (Manager) => {
|
|
9
|
+
return new Promise(async function (resolve) {
|
|
10
|
+
// Shortcuts
|
|
11
|
+
webManager = Manager.webManager;
|
|
12
|
+
|
|
13
|
+
// Initialize when DOM is ready
|
|
14
|
+
await webManager.dom().ready();
|
|
15
|
+
|
|
16
|
+
// Track update view
|
|
17
|
+
trackUpdateView();
|
|
18
|
+
|
|
19
|
+
// Resolve after initialization
|
|
20
|
+
return resolve();
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Tracking functions
|
|
25
|
+
function trackUpdateView() {
|
|
26
|
+
// Get version from URL or page data
|
|
27
|
+
const version = window.location.pathname.split('/').pop();
|
|
28
|
+
|
|
29
|
+
gtag('event', 'view_item', {
|
|
30
|
+
item_id: version,
|
|
31
|
+
item_name: `Version ${version}`,
|
|
32
|
+
item_category: 'update'
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -186,13 +186,22 @@
|
|
|
186
186
|
{% iftruthy page.resolved.theme.css.bundle %}
|
|
187
187
|
<link rel="stylesheet" type="text/css" href="{{ site.url }}/assets/css/{{ page.resolved.theme.css.bundle }}.bundle.css?cb={{ site.uj.cache_breaker }}"/>
|
|
188
188
|
{% endiftruthy %}
|
|
189
|
+
|
|
189
190
|
<!-- Otherwise, fetch main bundle -->
|
|
190
191
|
{% iffalsy page.resolved.theme.css.bundle %}
|
|
191
192
|
<link rel="stylesheet" type="text/css" href="{{ site.url }}/assets/css/main.bundle.css?cb={{ site.uj.cache_breaker }}"/>
|
|
192
193
|
{% endiffalsy %}
|
|
194
|
+
|
|
193
195
|
<!-- Then, fetch any page specific bundles -->
|
|
194
196
|
{% iffile page-css-path %}
|
|
195
197
|
<link rel="stylesheet" type="text/css" href="{{ site.url }}{{ page-css-path }}?cb={{ site.uj.cache_breaker }}"/>
|
|
198
|
+
|
|
199
|
+
<!-- Dev log -->
|
|
200
|
+
{% if jekyll.environment == "development" %}
|
|
201
|
+
<script>
|
|
202
|
+
console.info("Page-specific css loading: #main{{ page-css-path }}");
|
|
203
|
+
</script>
|
|
204
|
+
{% endif %}
|
|
196
205
|
{% endiffile %}
|
|
197
206
|
|
|
198
207
|
<!-- Style - Scripts are Disabled -->
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
### ALL PAGES ###
|
|
3
|
+
layout: themes/[ site.theme.id ]/frontend/pages/extension/index
|
|
4
|
+
|
|
5
|
+
### REGULAR PAGES ###
|
|
6
|
+
meta:
|
|
7
|
+
title: "{{ site.brand.name }} browser extension"
|
|
8
|
+
description: "The {{ site.brand.name }} browser extension is free to use on Chrome, Firefox, Edge & Opera. Get started for free today!"
|
|
9
|
+
breadcrumb: "Extension"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
{{ content | uj_content_format }}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
### ALL PAGES ###
|
|
3
|
+
layout: themes/[ site.theme.id ]/frontend/pages/extension/installed
|
|
4
|
+
|
|
5
|
+
### REGULAR PAGES ###
|
|
6
|
+
meta:
|
|
7
|
+
title: "Extension Installed - {{ site.brand.name }}"
|
|
8
|
+
description: "Thank you for installing the {{ site.brand.name }} browser extension! Learn how to pin it for quick access."
|
|
9
|
+
breadcrumb: "Installed"
|
|
10
|
+
robots: "noindex, nofollow"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
{{ content | uj_content_format }}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
### ALL PAGES ###
|
|
3
|
+
layout: themes/[ site.theme.id ]/frontend/pages/status
|
|
4
|
+
|
|
5
|
+
### REGULAR PAGES ###
|
|
6
|
+
meta:
|
|
7
|
+
title: "System Status - {{ site.brand.name }}"
|
|
8
|
+
description: "Check the current status and uptime of {{ site.brand.name }} services. View real-time system health and incident reports."
|
|
9
|
+
breadcrumb: "Status"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
{{ content | uj_content_format }}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
### ALL PAGES ###
|
|
3
|
+
layout: themes/[ site.theme.id ]/frontend/pages/updates/index
|
|
4
|
+
|
|
5
|
+
### REGULAR PAGES ###
|
|
6
|
+
meta:
|
|
7
|
+
title: "Updates - {{ site.brand.name }}"
|
|
8
|
+
description: "View release notes and version history for {{ site.brand.name }}. Stay up to date with the latest features and improvements."
|
|
9
|
+
breadcrumb: "Updates"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
{{ content | uj_content_format }}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
### ALL PAGES ###
|
|
3
|
+
layout: themes/[ site.theme.id ]/frontend/pages/updates/update
|
|
4
|
+
|
|
5
|
+
### REGULAR PAGES ###
|
|
6
|
+
meta:
|
|
7
|
+
title: "Version {{ page.update.version }} - {{ site.brand.name }}"
|
|
8
|
+
description: "{{ page.update.summary | default: 'Release notes for version ' | append: page.update.version | append: ' of ' | append: site.brand.name }}"
|
|
9
|
+
breadcrumb: "v{{ page.update.version }}"
|
|
10
|
+
|
|
11
|
+
# Asset path for all update pages
|
|
12
|
+
asset_path: updates/update
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{{ content | uj_content_format }}
|
|
@@ -121,7 +121,7 @@ badges:
|
|
|
121
121
|
<div class="d-flex align-items-center justify-content-between py-3">
|
|
122
122
|
<!-- Left: Brand -->
|
|
123
123
|
<a href="/" class="d-flex align-items-center text-decoration-none text-body">
|
|
124
|
-
<span class="avatar avatar-md me-3">
|
|
124
|
+
<span class="avatar avatar-md filter-adaptive me-3">
|
|
125
125
|
<img src="{{ site.brand.images.brandmark }}?cb={{ site.uj.cache_breaker }}" alt="{{ site.brand.name }}"/>
|
|
126
126
|
</span>
|
|
127
127
|
<h1 class="h5 mb-0 fw-semibold">{{ site.brand.name }} Account</h1>
|