diff --git a/test-footer-subscribe.html b/test-footer-subscribe.html new file mode 100644 index 0000000..765bad5 --- /dev/null +++ b/test-footer-subscribe.html @@ -0,0 +1,105 @@ + + + + + + Footer Subscribe Button Test + + + + +
+

Footer Subscribe Button - Fixed Layout

+

The subscriber count is now displayed below the button instead of inside it:

+ +
+
+

Without Subscribers

+

Button appears alone when subscriber count is 0

+
+
+

With Subscribers

+

Count appears below the button as secondary text

+
+
+
+ +
+ +
+ + + + \ No newline at end of file diff --git a/test-incident-subscriptions.html b/test-incident-subscriptions.html new file mode 100644 index 0000000..ad81342 --- /dev/null +++ b/test-incident-subscriptions.html @@ -0,0 +1,286 @@ + + + + + + Incident Subscription Test + + + + +
+

Incident Subscription Feature Demo

+ +
+

How Incident Subscriptions Work:

+ +
+ + + +
+ Event Log:
+
+
+ + + + \ No newline at end of file diff --git a/ts_web/elements/upl-statuspage-footer.ts b/ts_web/elements/upl-statuspage-footer.ts index 0346f3d..cbfcea2 100644 --- a/ts_web/elements/upl-statuspage-footer.ts +++ b/ts_web/elements/upl-statuspage-footer.ts @@ -298,10 +298,16 @@ export class UplStatuspageFooter extends DeesElement { border-color: ${colors.border.muted}; } + .subscribe-wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: ${unsafeCSS(spacing.xs)}; + } + .subscriber-count { font-size: 12px; - color: ${colors.text.muted}; - margin-top: ${unsafeCSS(spacing.xs)}; + color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; } .error-message { @@ -468,12 +474,14 @@ export class UplStatuspageFooter extends DeesElement { ` : ''} ${this.enableReportIssue ? html` diff --git a/ts_web/elements/upl-statuspage-incidents.demo.ts b/ts_web/elements/upl-statuspage-incidents.demo.ts index b7c4e80..3e260fb 100644 --- a/ts_web/elements/upl-statuspage-incidents.demo.ts +++ b/ts_web/elements/upl-statuspage-incidents.demo.ts @@ -521,6 +521,17 @@ export const demoFunc = () => html` } }); + // Handle incident subscriptions + incidents.addEventListener('incidentSubscribe', (event: CustomEvent) => { + console.log('Subscribed to incident:', event.detail); + logUpdate(`[SUBSCRIBED] User subscribed to incident "${event.detail.incidentTitle}"`); + }); + + incidents.addEventListener('incidentUnsubscribe', (event: CustomEvent) => { + console.log('Unsubscribed from incident:', event.detail); + logUpdate(`[UNSUBSCRIBED] User unsubscribed from incident "${event.detail.incident.title}"`); + }); + // Add update log const logDiv = document.createElement('div'); logDiv.className = 'incident-log'; @@ -1027,5 +1038,179 @@ export const demoFunc = () => html` + + +
+
Incident Subscription Management
+ { + const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any; + + // Sample incidents with subscription + const currentIncidents: IIncidentDetails[] = [ + { + id: 'sub-001', + title: 'Payment Gateway Intermittent Failures', + status: 'monitoring', + severity: 'major', + affectedServices: ['payment-gateway', 'checkout'], + startTime: Date.now() - 2 * 60 * 60 * 1000, + impact: 'Some customers may experience payment failures during checkout', + updates: [ + { + id: 'sub-u1', + timestamp: Date.now() - 2 * 60 * 60 * 1000, + status: 'investigating', + message: 'We are investigating reports of payment failures', + author: 'Payment Team' + }, + { + id: 'sub-u2', + timestamp: Date.now() - 1 * 60 * 60 * 1000, + status: 'identified', + message: 'Issue identified with payment processor API rate limits', + author: 'Payment Team' + }, + { + id: 'sub-u3', + timestamp: Date.now() - 30 * 60 * 1000, + status: 'monitoring', + message: 'Temporary fix applied, monitoring for stability', + author: 'Payment Team' + } + ] + }, + { + id: 'sub-002', + title: 'Email Delivery Delays', + status: 'identified', + severity: 'minor', + affectedServices: ['email-service'], + startTime: Date.now() - 45 * 60 * 1000, + impact: 'Transactional emails may be delayed by 5-10 minutes', + updates: [ + { + id: 'sub-u4', + timestamp: Date.now() - 45 * 60 * 1000, + status: 'investigating', + message: 'Investigating email queue backlog', + author: 'Infrastructure Team' + }, + { + id: 'sub-u5', + timestamp: Date.now() - 20 * 60 * 1000, + status: 'identified', + message: 'High volume causing queue delays, scaling up workers', + author: 'Infrastructure Team' + } + ] + } + ]; + + incidents.currentIncidents = currentIncidents; + incidents.pastIncidents = []; + + // Pre-subscribe to first incident + incidents.subscribedIncidentIds = ['sub-001']; + + // Create subscription status display + const statusDiv = document.createElement('div'); + statusDiv.className = 'subscription-status'; + statusDiv.style.cssText = ` + margin-top: 16px; + padding: 16px; + background: #f0f9ff; + border: 1px solid #bae6fd; + border-radius: 6px; + font-size: 14px; + `; + + const updateStatus = () => { + const subscribed = Array.from(incidents.subscribedIncidents || []); + statusDiv.innerHTML = ` + Subscription Status:
+ ${subscribed.length === 0 ? + 'Not subscribed to any incidents' : + `Subscribed to ${subscribed.length} incident(s):
` + + subscribed.map(id => { + const incident = currentIncidents.find(i => i.id === id); + return incident ? `• ${incident.title}` : ''; + }).filter(Boolean).join('
')} + `; + }; + + updateStatus(); + wrapperElement.appendChild(statusDiv); + + // Handle subscription events + incidents.addEventListener('incidentSubscribe', (event: CustomEvent) => { + console.log('Subscribed:', event.detail); + updateStatus(); + + // Show notification + const notification = document.createElement('div'); + notification.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + padding: 16px; + background: #10b981; + color: white; + border-radius: 6px; + box-shadow: 0 4px 6px rgba(0,0,0,0.1); + z-index: 1000; + animation: slideIn 0.3s ease; + `; + notification.textContent = `✓ Subscribed to: ${event.detail.incidentTitle}`; + document.body.appendChild(notification); + setTimeout(() => notification.remove(), 3000); + }); + + incidents.addEventListener('incidentUnsubscribe', (event: CustomEvent) => { + console.log('Unsubscribed:', event.detail); + updateStatus(); + + // Show notification + const notification = document.createElement('div'); + notification.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + padding: 16px; + background: #6b7280; + color: white; + border-radius: 6px; + box-shadow: 0 4px 6px rgba(0,0,0,0.1); + z-index: 1000; + animation: slideIn 0.3s ease; + `; + notification.textContent = `Unsubscribed from: ${event.detail.incident.title}`; + document.body.appendChild(notification); + setTimeout(() => notification.remove(), 3000); + }); + + // Add info text + const infoDiv = document.createElement('div'); + infoDiv.style.cssText = ` + margin-top: 12px; + padding: 12px; + background: #f3f4f6; + border-radius: 4px; + font-size: 13px; + color: #6b7280; + `; + infoDiv.innerHTML = ` + How it works:
+ • Click on an incident to expand it
+ • Click "Subscribe to updates" to get notifications
+ • Subscribed incidents show a checkmark
+ • Click again to unsubscribe + `; + wrapperElement.appendChild(infoDiv); + }} + > + +
+
`; \ No newline at end of file diff --git a/ts_web/elements/upl-statuspage-incidents.ts b/ts_web/elements/upl-statuspage-incidents.ts index 8d757b9..73aaa1f 100644 --- a/ts_web/elements/upl-statuspage-incidents.ts +++ b/ts_web/elements/upl-statuspage-incidents.ts @@ -51,16 +51,42 @@ export class UplStatuspageIncidents extends DeesElement { }) public daysToShow = 90; + @property({ + type: Array, + }) + public subscribedIncidentIds: string[] = []; + @property({ type: Object, state: true, }) private expandedIncidents: Set = new Set(); + @property({ + type: Object, + state: true, + }) + private subscribedIncidents: Set = new Set(); + constructor() { super(); } + async connectedCallback() { + await super.connectedCallback(); + // Initialize subscribed incidents from the property + if (this.subscribedIncidentIds.length > 0) { + this.subscribedIncidents = new Set(this.subscribedIncidentIds); + } + } + + updated(changedProperties: Map) { + super.updated(changedProperties); + if (changedProperties.has('subscribedIncidentIds')) { + this.subscribedIncidents = new Set(this.subscribedIncidentIds); + } + } + public static styles = [ plugins.domtools.elementBasic.staticStyles, commonStyles, @@ -312,6 +338,46 @@ export class UplStatuspageIncidents extends DeesElement { .show-more-button:active { transform: translateY(0); } + + .incident-actions { + display: flex; + gap: ${unsafeCSS(spacing.md)}; + align-items: center; + margin-top: ${unsafeCSS(spacing.lg)}; + padding-top: ${unsafeCSS(spacing.lg)}; + border-top: 1px solid ${colors.border.default}; + } + + .subscribe-button { + display: inline-flex; + align-items: center; + gap: ${unsafeCSS(spacing.xs)}; + padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.md)}; + background: transparent; + border: 1px solid ${colors.border.default}; + border-radius: ${unsafeCSS(borderRadius.base)}; + cursor: pointer; + font-size: 13px; + font-weight: 400; + transition: all 0.2s ease; + color: ${colors.text.primary}; + font-family: ${unsafeCSS(fonts.base)}; + } + + .subscribe-button:hover { + background: ${colors.background.secondary}; + border-color: ${colors.border.muted}; + } + + .subscribe-button.subscribed { + background: ${cssManager.bdTheme('#f0fdf4', '#064e3b')}; + border-color: ${cssManager.bdTheme('#86efac', '#047857')}; + color: ${cssManager.bdTheme('#047857', '#86efac')}; + } + + .subscribe-button.subscribed:hover { + background: ${cssManager.bdTheme('#dcfce7', '#065f46')}; + } .collapsed-hint { font-size: 12px; @@ -470,6 +536,35 @@ export class UplStatuspageIncidents extends DeesElement { Resolution: ${incident.resolution} ` : ''} + +
+ + ${isCurrent ? html` + Get notified when this incident is updated or resolved + ` : ''} +
` : ''} @@ -570,6 +665,38 @@ export class UplStatuspageIncidents extends DeesElement { console.log('Show more incidents'); } + private isSubscribedToIncident(incidentId: string): boolean { + return this.subscribedIncidents.has(incidentId); + } + + private handleIncidentSubscribe(incident: IIncidentDetails) { + const newSubscribed = new Set(this.subscribedIncidents); + if (newSubscribed.has(incident.id)) { + newSubscribed.delete(incident.id); + this.dispatchEvent(new CustomEvent('incidentUnsubscribe', { + detail: { + incident, + incidentId: incident.id + }, + bubbles: true, + composed: true + })); + } else { + newSubscribed.add(incident.id); + this.dispatchEvent(new CustomEvent('incidentSubscribe', { + detail: { + incident, + incidentId: incident.id, + incidentTitle: incident.title, + affectedServices: incident.affectedServices + }, + bubbles: true, + composed: true + })); + } + this.subscribedIncidents = newSubscribed; + } + public dispatchReportNewIncident() { this.dispatchEvent(new CustomEvent('reportNewIncident', { bubbles: true,