This commit is contained in:
2025-06-30 07:54:17 +00:00
parent f9604263e3
commit 891eb04d11
5 changed files with 716 additions and 5 deletions

View File

@@ -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 {
<div class="footer-actions">
${this.enableSubscribe ? html`
<button class="action-button" @click=${this.handleSubscribeClick}>
Subscribe to Updates
<div class="subscribe-wrapper">
<button class="action-button" @click=${this.handleSubscribeClick}>
Subscribe to Updates
</button>
${this.subscriberCount > 0 ? html`
<div class="subscriber-count">${this.subscriberCount.toLocaleString()} subscribers</div>
` : ''}
</button>
</div>
` : ''}
${this.enableReportIssue ? html`

View File

@@ -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`
<upl-statuspage-incidents></upl-statuspage-incidents>
</dees-demowrapper>
</div>
<!-- Incident Subscription Demo -->
<div class="demo-section">
<div class="demo-title">Incident Subscription Management</div>
<dees-demowrapper
.runAfterRender=${async (wrapperElement: any) => {
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 = `
<strong>Subscription Status:</strong><br>
${subscribed.length === 0 ?
'Not subscribed to any incidents' :
`Subscribed to ${subscribed.length} incident(s):<br>` +
subscribed.map(id => {
const incident = currentIncidents.find(i => i.id === id);
return incident ? `${incident.title}` : '';
}).filter(Boolean).join('<br>')}
`;
};
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 = `
<strong>How it works:</strong><br>
Click on an incident to expand it<br>
Click "Subscribe to updates" to get notifications<br>
Subscribed incidents show a checkmark<br>
Click again to unsubscribe
`;
wrapperElement.appendChild(infoDiv);
}}
>
<upl-statuspage-incidents></upl-statuspage-incidents>
</dees-demowrapper>
</div>
</div>
`;

View File

@@ -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<string> = new Set();
@property({
type: Object,
state: true,
})
private subscribedIncidents: Set<string> = 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<string, any>) {
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 {
<strong>Resolution:</strong> ${incident.resolution}
</div>
` : ''}
<div class="incident-actions">
<button
class="subscribe-button ${this.isSubscribedToIncident(incident.id) ? 'subscribed' : ''}"
@click=${(e: Event) => {
e.stopPropagation();
this.handleIncidentSubscribe(incident);
}}
>
${this.isSubscribedToIncident(incident.id) ? html`
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.6667 3.5L5.25 9.91667L2.33334 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Subscribed to updates
` : html`
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5 5.25V8.75C10.5 9.34674 10.2629 9.91903 9.84099 10.341C9.41903 10.7629 8.84674 11 8.25 11L3.75 11C3.15326 11 2.58097 10.7629 2.15901 10.341C1.73705 9.91903 1.5 9.34674 1.5 8.75V4.25C1.5 3.65326 1.73705 3.08097 2.15901 2.65901C2.58097 2.23705 3.15326 2 3.75 2H7.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 1.5H12.5M12.5 1.5V5M12.5 1.5L6 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Subscribe to updates
`}
</button>
${isCurrent ? html`
<span style="
font-size: 12px;
color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')};
">Get notified when this incident is updated or resolved</span>
` : ''}
</div>
</div>
` : ''}
</div>
@@ -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,