2022-03-24 16:07:15 +01:00
|
|
|
import * as plugins from '../plugins.js';
|
2021-09-27 00:49:30 +02:00
|
|
|
import {
|
|
|
|
|
DeesElement,
|
|
|
|
|
property,
|
|
|
|
|
html,
|
|
|
|
|
customElement,
|
2024-06-26 20:28:09 +02:00
|
|
|
type TemplateResult,
|
2021-09-27 00:49:30 +02:00
|
|
|
css,
|
|
|
|
|
cssManager,
|
2025-06-29 19:55:58 +00:00
|
|
|
unsafeCSS,
|
2024-06-26 20:28:09 +02:00
|
|
|
} from '@design.estate/dees-element';
|
2025-06-29 19:55:58 +00:00
|
|
|
import type { IIncidentDetails } from '../interfaces/index.js';
|
2025-12-23 09:26:37 +00:00
|
|
|
import * as sharedStyles from '../styles/shared.styles.js';
|
2025-06-29 19:55:58 +00:00
|
|
|
import './internal/uplinternal-miniheading.js';
|
|
|
|
|
import { demoFunc } from './upl-statuspage-incidents.demo.js';
|
2021-09-27 00:45:17 +02:00
|
|
|
|
|
|
|
|
declare global {
|
|
|
|
|
interface HTMLElementTagNameMap {
|
|
|
|
|
'upl-statuspage-incidents': UplStatuspageIncidents;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 10:12:29 +00:00
|
|
|
type TIncidentStatus = 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
|
|
|
|
|
|
2021-09-27 00:45:17 +02:00
|
|
|
@customElement('upl-statuspage-incidents')
|
|
|
|
|
export class UplStatuspageIncidents extends DeesElement {
|
|
|
|
|
// STATIC
|
2025-06-29 19:55:58 +00:00
|
|
|
public static demo = demoFunc;
|
2021-09-27 00:45:17 +02:00
|
|
|
|
|
|
|
|
// INSTANCE
|
|
|
|
|
@property({
|
2021-09-27 00:49:30 +02:00
|
|
|
type: Array,
|
2021-09-27 00:45:17 +02:00
|
|
|
})
|
2025-12-23 09:26:37 +00:00
|
|
|
accessor currentIncidents: IIncidentDetails[] = [];
|
2021-09-27 00:45:17 +02:00
|
|
|
|
|
|
|
|
@property({
|
2021-09-27 00:49:30 +02:00
|
|
|
type: Array,
|
2021-09-27 00:45:17 +02:00
|
|
|
})
|
2025-12-23 09:26:37 +00:00
|
|
|
accessor pastIncidents: IIncidentDetails[] = [];
|
2021-09-27 00:45:17 +02:00
|
|
|
|
|
|
|
|
@property({
|
2021-09-27 00:49:30 +02:00
|
|
|
type: Boolean,
|
2021-09-27 00:45:17 +02:00
|
|
|
})
|
2025-12-23 09:26:37 +00:00
|
|
|
accessor whitelabel = false;
|
2021-09-27 00:45:17 +02:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
@property({
|
|
|
|
|
type: Boolean,
|
|
|
|
|
})
|
2025-12-23 09:26:37 +00:00
|
|
|
accessor loading = false;
|
2025-06-29 19:55:58 +00:00
|
|
|
|
|
|
|
|
@property({
|
|
|
|
|
type: Number,
|
|
|
|
|
})
|
2025-12-23 09:26:37 +00:00
|
|
|
accessor daysToShow = 90;
|
2025-06-29 19:55:58 +00:00
|
|
|
|
2025-06-30 07:54:17 +00:00
|
|
|
@property({
|
|
|
|
|
type: Array,
|
|
|
|
|
})
|
2025-12-23 09:26:37 +00:00
|
|
|
accessor subscribedIncidentIds: string[] = [];
|
2025-06-30 07:54:17 +00:00
|
|
|
|
2025-06-29 23:43:17 +00:00
|
|
|
@property({
|
|
|
|
|
type: Object,
|
|
|
|
|
state: true,
|
|
|
|
|
})
|
2025-12-23 09:26:37 +00:00
|
|
|
private accessor expandedIncidents: Set<string> = new Set();
|
2025-06-29 23:43:17 +00:00
|
|
|
|
2025-06-30 07:54:17 +00:00
|
|
|
@property({
|
|
|
|
|
type: Object,
|
|
|
|
|
state: true,
|
|
|
|
|
})
|
2025-12-23 09:26:37 +00:00
|
|
|
private accessor subscribedIncidents: Set<string> = new Set();
|
2025-06-30 07:54:17 +00:00
|
|
|
|
2025-12-24 10:12:29 +00:00
|
|
|
private statusIcons: Record<TIncidentStatus, string> = {
|
|
|
|
|
investigating: 'lucide:Search',
|
|
|
|
|
identified: 'lucide:Target',
|
|
|
|
|
monitoring: 'lucide:Eye',
|
|
|
|
|
resolved: 'lucide:CheckCircle',
|
|
|
|
|
postmortem: 'lucide:FileText',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private statusLabels: Record<TIncidentStatus, string> = {
|
|
|
|
|
investigating: 'Investigating',
|
|
|
|
|
identified: 'Identified',
|
|
|
|
|
monitoring: 'Monitoring',
|
|
|
|
|
resolved: 'Resolved',
|
|
|
|
|
postmortem: 'Postmortem',
|
|
|
|
|
};
|
|
|
|
|
|
2021-09-27 00:45:17 +02:00
|
|
|
constructor() {
|
|
|
|
|
super();
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-30 07:54:17 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-27 00:45:17 +02:00
|
|
|
public static styles = [
|
|
|
|
|
plugins.domtools.elementBasic.staticStyles,
|
2025-12-23 09:26:37 +00:00
|
|
|
sharedStyles.commonStyles,
|
2021-09-27 00:45:17 +02:00
|
|
|
css`
|
|
|
|
|
:host {
|
2021-09-27 00:49:30 +02:00
|
|
|
display: block;
|
2025-06-29 19:55:58 +00:00
|
|
|
background: transparent;
|
2025-12-23 09:26:37 +00:00
|
|
|
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
|
|
|
|
|
color: ${sharedStyles.colors.text.primary};
|
2021-09-27 00:49:30 +02:00
|
|
|
}
|
2021-09-27 00:45:17 +02:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
.container {
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
margin: 0 auto;
|
2025-12-23 09:26:37 +00:00
|
|
|
padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
|
2021-09-27 00:49:30 +02:00
|
|
|
}
|
2021-09-27 00:45:17 +02:00
|
|
|
|
2021-09-27 00:49:30 +02:00
|
|
|
.noIncidentBox {
|
2025-12-23 09:26:37 +00:00
|
|
|
background: ${sharedStyles.colors.background.card};
|
|
|
|
|
padding: ${unsafeCSS(sharedStyles.spacing.xl)};
|
|
|
|
|
margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
|
2025-12-23 19:16:17 +00:00
|
|
|
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
|
2025-12-23 09:26:37 +00:00
|
|
|
border: 1px solid ${sharedStyles.colors.border.default};
|
2025-06-29 19:55:58 +00:00
|
|
|
text-align: center;
|
2025-12-23 09:26:37 +00:00
|
|
|
color: ${sharedStyles.colors.text.secondary};
|
|
|
|
|
box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
|
2025-12-23 19:16:17 +00:00
|
|
|
animation: fadeInUp 0.4s ${unsafeCSS(sharedStyles.easings.default)} both;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-23 19:16:17 +00:00
|
|
|
/* Staggered entrance animations */
|
2025-06-29 19:55:58 +00:00
|
|
|
.incident-card {
|
2025-12-23 09:26:37 +00:00
|
|
|
background: ${sharedStyles.colors.background.card};
|
2025-12-23 19:16:17 +00:00
|
|
|
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
|
2025-12-23 09:26:37 +00:00
|
|
|
margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
|
2025-06-29 19:55:58 +00:00
|
|
|
overflow: hidden;
|
2025-12-23 09:26:37 +00:00
|
|
|
box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
|
|
|
|
|
border: 1px solid ${sharedStyles.colors.border.default};
|
2025-12-23 19:16:17 +00:00
|
|
|
transition: all ${unsafeCSS(sharedStyles.durations.normal)} ${unsafeCSS(sharedStyles.easings.default)};
|
|
|
|
|
animation: fadeInUp 0.4s ${unsafeCSS(sharedStyles.easings.default)} both;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-23 19:16:17 +00:00
|
|
|
.incident-card:nth-child(1) { animation-delay: 0ms; }
|
|
|
|
|
.incident-card:nth-child(2) { animation-delay: 50ms; }
|
|
|
|
|
.incident-card:nth-child(3) { animation-delay: 100ms; }
|
|
|
|
|
.incident-card:nth-child(4) { animation-delay: 150ms; }
|
|
|
|
|
.incident-card:nth-child(5) { animation-delay: 200ms; }
|
|
|
|
|
|
|
|
|
|
.incident-card:hover {
|
|
|
|
|
transform: translateY(-2px);
|
2025-12-23 09:26:37 +00:00
|
|
|
box-shadow: ${unsafeCSS(sharedStyles.shadows.md)};
|
2025-12-23 19:16:17 +00:00
|
|
|
border-color: ${sharedStyles.colors.border.muted};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-card.expanded {
|
|
|
|
|
box-shadow: ${unsafeCSS(sharedStyles.shadows.lg)};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Active incident pulse effect */
|
|
|
|
|
.incident-card.active-incident {
|
|
|
|
|
animation: fadeInUp 0.4s ${unsafeCSS(sharedStyles.easings.default)} both,
|
|
|
|
|
incident-pulse 3s ease-in-out infinite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes fadeInUp {
|
|
|
|
|
from {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transform: translateY(16px);
|
|
|
|
|
}
|
|
|
|
|
to {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes incident-pulse {
|
|
|
|
|
0%, 100% {
|
|
|
|
|
box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
|
|
|
|
|
}
|
|
|
|
|
50% {
|
|
|
|
|
box-shadow: ${unsafeCSS(sharedStyles.shadows.md)},
|
|
|
|
|
0 0 0 2px ${cssManager.bdTheme('rgba(239, 68, 68, 0.15)', 'rgba(248, 113, 113, 0.2)')};
|
|
|
|
|
}
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-24 10:12:29 +00:00
|
|
|
/* New header layout matching admin catalog */
|
2025-06-29 19:55:58 +00:00
|
|
|
.incident-header {
|
|
|
|
|
display: flex;
|
2025-12-24 10:12:29 +00:00
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
padding: 16px;
|
2025-06-29 23:43:17 +00:00
|
|
|
cursor: pointer;
|
2025-12-23 19:16:17 +00:00
|
|
|
transition: background-color ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
|
2025-06-29 23:43:17 +00:00
|
|
|
}
|
2025-12-23 09:26:37 +00:00
|
|
|
|
2025-06-29 23:43:17 +00:00
|
|
|
.incident-header:hover {
|
|
|
|
|
background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.02)', 'rgba(255, 255, 255, 0.02)')};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-24 10:12:29 +00:00
|
|
|
/* Internal severity bar (replacing border-left) */
|
|
|
|
|
.incident-severity {
|
|
|
|
|
width: 4px;
|
|
|
|
|
align-self: stretch;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
flex-shrink: 0;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-24 10:12:29 +00:00
|
|
|
.incident-severity.critical { background: ${sharedStyles.colors.status.major}; }
|
|
|
|
|
.incident-severity.major { background: ${sharedStyles.colors.status.partial}; }
|
|
|
|
|
.incident-severity.minor { background: ${sharedStyles.colors.status.degraded}; }
|
|
|
|
|
.incident-severity.maintenance { background: ${sharedStyles.colors.status.maintenance}; }
|
2025-06-29 19:55:58 +00:00
|
|
|
|
2025-12-24 10:12:29 +00:00
|
|
|
.incident-main {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-24 10:12:29 +00:00
|
|
|
.incident-title-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
flex-wrap: wrap;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-title {
|
2025-12-24 10:12:29 +00:00
|
|
|
font-size: 15px;
|
2025-06-29 19:55:58 +00:00
|
|
|
font-weight: 600;
|
|
|
|
|
margin: 0;
|
2025-12-24 10:12:29 +00:00
|
|
|
color: ${sharedStyles.colors.text.primary};
|
2025-12-23 19:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-24 10:12:29 +00:00
|
|
|
/* Status badge inline with title */
|
2025-06-29 19:55:58 +00:00
|
|
|
.incident-status {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
2025-12-23 19:16:17 +00:00
|
|
|
gap: 6px;
|
2025-12-24 10:12:29 +00:00
|
|
|
padding: 4px 10px;
|
2025-12-23 19:16:17 +00:00
|
|
|
font-size: 11px;
|
2025-12-24 10:12:29 +00:00
|
|
|
font-weight: 500;
|
|
|
|
|
border-radius: 9999px;
|
2025-06-29 19:55:58 +00:00
|
|
|
text-transform: uppercase;
|
|
|
|
|
flex-shrink: 0;
|
2025-12-24 10:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-status dees-icon {
|
|
|
|
|
--icon-size: 12px;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-status.investigating {
|
2025-12-24 10:12:29 +00:00
|
|
|
background: ${cssManager.bdTheme('rgba(249, 115, 22, 0.1)', 'rgba(249, 115, 22, 0.2)')};
|
|
|
|
|
color: ${cssManager.bdTheme('#ea580c', '#fb923c')};
|
|
|
|
|
--icon-color: ${cssManager.bdTheme('#ea580c', '#fb923c')};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-status.identified {
|
2025-12-24 10:12:29 +00:00
|
|
|
background: ${cssManager.bdTheme('rgba(234, 179, 8, 0.1)', 'rgba(234, 179, 8, 0.2)')};
|
|
|
|
|
color: ${cssManager.bdTheme('#ca8a04', '#facc15')};
|
|
|
|
|
--icon-color: ${cssManager.bdTheme('#ca8a04', '#facc15')};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-status.monitoring {
|
2025-12-24 10:12:29 +00:00
|
|
|
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.1)', 'rgba(59, 130, 246, 0.2)')};
|
|
|
|
|
color: ${cssManager.bdTheme('#2563eb', '#60a5fa')};
|
|
|
|
|
--icon-color: ${cssManager.bdTheme('#2563eb', '#60a5fa')};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-status.resolved {
|
2025-12-24 10:12:29 +00:00
|
|
|
background: ${cssManager.bdTheme('rgba(34, 197, 94, 0.1)', 'rgba(34, 197, 94, 0.2)')};
|
|
|
|
|
color: ${cssManager.bdTheme('#16a34a', '#4ade80')};
|
|
|
|
|
--icon-color: ${cssManager.bdTheme('#16a34a', '#4ade80')};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-status.postmortem {
|
2025-12-24 10:12:29 +00:00
|
|
|
background: ${cssManager.bdTheme('rgba(168, 85, 247, 0.1)', 'rgba(168, 85, 247, 0.2)')};
|
|
|
|
|
color: ${cssManager.bdTheme('#9333ea', '#c084fc')};
|
|
|
|
|
--icon-color: ${cssManager.bdTheme('#9333ea', '#c084fc')};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-meta {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: ${sharedStyles.colors.text.secondary};
|
|
|
|
|
flex-wrap: wrap;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-24 10:12:29 +00:00
|
|
|
.incident-meta-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
2025-12-23 19:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-24 10:12:29 +00:00
|
|
|
.incident-meta-item dees-icon {
|
|
|
|
|
--icon-size: 12px;
|
|
|
|
|
--icon-color: ${sharedStyles.colors.text.muted};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-expand {
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: ${sharedStyles.colors.text.muted};
|
|
|
|
|
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-expand:hover {
|
|
|
|
|
background: ${sharedStyles.colors.background.muted};
|
|
|
|
|
color: ${sharedStyles.colors.text.primary};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-expand dees-icon {
|
|
|
|
|
--icon-size: 16px;
|
|
|
|
|
transition: transform ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-expand.expanded dees-icon {
|
|
|
|
|
transform: rotate(180deg);
|
2025-12-23 19:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
.incident-body {
|
2025-12-24 10:12:29 +00:00
|
|
|
padding: 0 16px 16px 36px;
|
2025-12-23 19:16:17 +00:00
|
|
|
animation: slideDown 0.3s ${unsafeCSS(sharedStyles.easings.default)};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes slideDown {
|
|
|
|
|
from {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transform: translateY(-8px);
|
|
|
|
|
}
|
|
|
|
|
to {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
}
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.incident-impact {
|
2025-12-23 09:26:37 +00:00
|
|
|
margin: ${unsafeCSS(sharedStyles.spacing.md)} 0;
|
2025-12-23 19:16:17 +00:00
|
|
|
padding: ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.lg)};
|
2025-12-23 09:26:37 +00:00
|
|
|
background: ${sharedStyles.colors.background.secondary};
|
|
|
|
|
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
|
2025-06-29 19:55:58 +00:00
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 1.6;
|
2025-12-23 19:16:17 +00:00
|
|
|
border-left: 3px solid ${sharedStyles.colors.border.muted};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.affected-services {
|
2025-12-23 19:16:17 +00:00
|
|
|
margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.affected-services-title {
|
2025-12-23 19:16:17 +00:00
|
|
|
font-size: 12px;
|
2025-06-29 19:55:58 +00:00
|
|
|
font-weight: 600;
|
2025-12-23 09:26:37 +00:00
|
|
|
margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
|
2025-12-23 19:16:17 +00:00
|
|
|
color: ${sharedStyles.colors.text.secondary};
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.service-tag {
|
|
|
|
|
display: inline-block;
|
2025-12-23 19:16:17 +00:00
|
|
|
padding: 4px 10px;
|
2025-06-29 19:55:58 +00:00
|
|
|
margin: 2px;
|
2025-12-23 09:26:37 +00:00
|
|
|
background: ${sharedStyles.colors.background.muted};
|
|
|
|
|
border-radius: ${unsafeCSS(sharedStyles.borderRadius.sm)};
|
2025-06-29 19:55:58 +00:00
|
|
|
font-size: 12px;
|
2025-12-23 09:26:37 +00:00
|
|
|
color: ${sharedStyles.colors.text.secondary};
|
2025-12-23 19:16:17 +00:00
|
|
|
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-23 19:16:17 +00:00
|
|
|
.service-tag:hover {
|
|
|
|
|
background: ${sharedStyles.colors.background.secondary};
|
|
|
|
|
color: ${sharedStyles.colors.text.primary};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Timeline visualization for updates */
|
2025-06-29 19:55:58 +00:00
|
|
|
.incident-updates {
|
2025-12-23 19:16:17 +00:00
|
|
|
margin-top: ${unsafeCSS(sharedStyles.spacing.xl)};
|
2025-12-23 09:26:37 +00:00
|
|
|
border-top: 1px solid ${sharedStyles.colors.border.default};
|
|
|
|
|
padding-top: ${unsafeCSS(sharedStyles.spacing.lg)};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-23 19:16:17 +00:00
|
|
|
.updates-title {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
margin: 0 0 ${unsafeCSS(sharedStyles.spacing.lg)} 0;
|
|
|
|
|
color: ${sharedStyles.colors.text.secondary};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.timeline {
|
|
|
|
|
position: relative;
|
|
|
|
|
padding-left: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
.update-item {
|
|
|
|
|
position: relative;
|
2025-12-23 09:26:37 +00:00
|
|
|
padding-left: ${unsafeCSS(sharedStyles.spacing.lg)};
|
2025-12-23 19:16:17 +00:00
|
|
|
padding-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
|
|
|
|
|
animation: fadeInUp 0.3s ${unsafeCSS(sharedStyles.easings.default)} both;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.update-item:last-child {
|
|
|
|
|
padding-bottom: 0;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-23 21:11:55 +00:00
|
|
|
.update-item:not(:last-child)::after {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: -15px;
|
|
|
|
|
top: 18px;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
width: 2px;
|
|
|
|
|
background: ${sharedStyles.colors.border.default};
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
.update-item::before {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
2025-12-23 19:16:17 +00:00
|
|
|
left: -22px;
|
|
|
|
|
top: 4px;
|
|
|
|
|
width: 12px;
|
|
|
|
|
height: 12px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: ${sharedStyles.colors.background.card};
|
|
|
|
|
border: 2px solid ${sharedStyles.colors.border.muted};
|
|
|
|
|
z-index: 1;
|
|
|
|
|
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.update-item:first-child::before {
|
|
|
|
|
border-color: ${sharedStyles.colors.status.operational};
|
|
|
|
|
background: ${sharedStyles.colors.status.operational};
|
|
|
|
|
box-shadow: 0 0 0 3px ${cssManager.bdTheme('rgba(22, 163, 74, 0.15)', 'rgba(34, 197, 94, 0.2)')};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.update-item:hover::before {
|
|
|
|
|
transform: scale(1.2);
|
|
|
|
|
border-color: ${sharedStyles.colors.text.secondary};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.update-time {
|
2025-12-23 19:16:17 +00:00
|
|
|
font-size: 11px;
|
|
|
|
|
color: ${sharedStyles.colors.text.muted};
|
|
|
|
|
margin-bottom: 4px;
|
2025-12-23 09:26:37 +00:00
|
|
|
font-family: ${unsafeCSS(sharedStyles.fonts.mono)};
|
2025-12-23 19:16:17 +00:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.update-status-badge {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.02em;
|
|
|
|
|
background: ${sharedStyles.colors.background.muted};
|
|
|
|
|
color: ${sharedStyles.colors.text.secondary};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.update-message {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 1.6;
|
2025-12-23 09:26:37 +00:00
|
|
|
color: ${sharedStyles.colors.text.primary};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.update-author {
|
|
|
|
|
font-size: 12px;
|
2025-12-23 19:16:17 +00:00
|
|
|
color: ${sharedStyles.colors.text.muted};
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.loading-skeleton {
|
|
|
|
|
height: 140px;
|
|
|
|
|
background: ${cssManager.bdTheme(
|
|
|
|
|
'linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%)',
|
|
|
|
|
'linear-gradient(90deg, #1f1f1f 25%, #262626 50%, #1f1f1f 75%)'
|
|
|
|
|
)};
|
|
|
|
|
background-size: 200% 100%;
|
2025-12-23 19:16:17 +00:00
|
|
|
animation: shimmer 1.5s infinite;
|
|
|
|
|
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
|
2025-12-23 09:26:37 +00:00
|
|
|
margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-23 19:16:17 +00:00
|
|
|
@keyframes shimmer {
|
2025-06-29 19:55:58 +00:00
|
|
|
0% { background-position: 200% 0; }
|
|
|
|
|
100% { background-position: -200% 0; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.show-more {
|
|
|
|
|
text-align: center;
|
2025-12-23 19:16:17 +00:00
|
|
|
margin-top: ${unsafeCSS(sharedStyles.spacing.xl)};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.show-more-button {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2025-12-23 19:16:17 +00:00
|
|
|
gap: 8px;
|
|
|
|
|
padding: 10px 20px;
|
2025-06-29 19:55:58 +00:00
|
|
|
background: transparent;
|
2025-12-23 09:26:37 +00:00
|
|
|
border: 1px solid ${sharedStyles.colors.border.default};
|
|
|
|
|
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
|
2025-06-29 19:55:58 +00:00
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 500;
|
2025-12-23 19:16:17 +00:00
|
|
|
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
|
2025-12-23 09:26:37 +00:00
|
|
|
color: ${sharedStyles.colors.text.primary};
|
|
|
|
|
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.show-more-button:hover {
|
2025-12-23 09:26:37 +00:00
|
|
|
background: ${sharedStyles.colors.background.secondary};
|
|
|
|
|
border-color: ${sharedStyles.colors.border.muted};
|
2025-12-23 19:16:17 +00:00
|
|
|
transform: translateY(-2px);
|
|
|
|
|
box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.show-more-button:active {
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
}
|
2025-12-23 09:26:37 +00:00
|
|
|
|
2025-06-30 07:54:17 +00:00
|
|
|
.incident-actions {
|
|
|
|
|
display: flex;
|
2025-12-23 09:26:37 +00:00
|
|
|
gap: ${unsafeCSS(sharedStyles.spacing.md)};
|
2025-06-30 07:54:17 +00:00
|
|
|
align-items: center;
|
2025-12-23 09:26:37 +00:00
|
|
|
margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
|
|
|
|
|
padding-top: ${unsafeCSS(sharedStyles.spacing.lg)};
|
|
|
|
|
border-top: 1px solid ${sharedStyles.colors.border.default};
|
2025-06-30 07:54:17 +00:00
|
|
|
}
|
2025-12-23 09:26:37 +00:00
|
|
|
|
2025-06-30 07:54:17 +00:00
|
|
|
.subscribe-button {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
2025-12-23 19:16:17 +00:00
|
|
|
gap: 6px;
|
|
|
|
|
padding: 8px 14px;
|
2025-06-30 07:54:17 +00:00
|
|
|
background: transparent;
|
2025-12-23 09:26:37 +00:00
|
|
|
border: 1px solid ${sharedStyles.colors.border.default};
|
|
|
|
|
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
|
2025-06-30 07:54:17 +00:00
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 13px;
|
2025-12-23 19:16:17 +00:00
|
|
|
font-weight: 500;
|
|
|
|
|
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
|
2025-12-23 09:26:37 +00:00
|
|
|
color: ${sharedStyles.colors.text.primary};
|
|
|
|
|
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
|
2025-06-30 07:54:17 +00:00
|
|
|
}
|
2025-12-23 09:26:37 +00:00
|
|
|
|
2025-06-30 07:54:17 +00:00
|
|
|
.subscribe-button:hover {
|
2025-12-23 09:26:37 +00:00
|
|
|
background: ${sharedStyles.colors.background.secondary};
|
|
|
|
|
border-color: ${sharedStyles.colors.border.muted};
|
2025-12-23 19:16:17 +00:00
|
|
|
transform: translateY(-1px);
|
2025-06-30 07:54:17 +00:00
|
|
|
}
|
2025-12-23 19:16:17 +00:00
|
|
|
|
2025-06-30 07:54:17 +00:00
|
|
|
.subscribe-button.subscribed {
|
|
|
|
|
background: ${cssManager.bdTheme('#f0fdf4', '#064e3b')};
|
|
|
|
|
border-color: ${cssManager.bdTheme('#86efac', '#047857')};
|
|
|
|
|
color: ${cssManager.bdTheme('#047857', '#86efac')};
|
|
|
|
|
}
|
2025-12-23 19:16:17 +00:00
|
|
|
|
2025-06-30 07:54:17 +00:00
|
|
|
.subscribe-button.subscribed:hover {
|
|
|
|
|
background: ${cssManager.bdTheme('#dcfce7', '#065f46')};
|
|
|
|
|
}
|
2025-06-29 19:55:58 +00:00
|
|
|
|
|
|
|
|
@media (max-width: 640px) {
|
|
|
|
|
.container {
|
2025-12-23 09:26:37 +00:00
|
|
|
padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
2025-12-23 09:26:37 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
.incident-header {
|
2025-12-24 10:12:29 +00:00
|
|
|
padding: 12px;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-23 19:16:17 +00:00
|
|
|
.incident-body {
|
2025-12-24 10:12:29 +00:00
|
|
|
padding: 0 12px 12px 24px;
|
2025-12-23 19:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
.incident-meta {
|
|
|
|
|
flex-direction: column;
|
2025-12-24 10:12:29 +00:00
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: 8px;
|
2025-06-29 19:55:58 +00:00
|
|
|
}
|
2025-12-23 19:16:17 +00:00
|
|
|
|
|
|
|
|
.timeline {
|
|
|
|
|
padding-left: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.update-item::before {
|
|
|
|
|
left: -18px;
|
|
|
|
|
width: 10px;
|
|
|
|
|
height: 10px;
|
|
|
|
|
}
|
2025-12-23 21:11:55 +00:00
|
|
|
|
|
|
|
|
.update-item:not(:last-child)::after {
|
|
|
|
|
left: -12px;
|
|
|
|
|
top: 16px;
|
|
|
|
|
}
|
2021-09-27 00:49:30 +02:00
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
];
|
2021-09-27 00:45:17 +02:00
|
|
|
|
|
|
|
|
public render(): TemplateResult {
|
|
|
|
|
return html`
|
2025-06-29 19:55:58 +00:00
|
|
|
<div class="container">
|
|
|
|
|
<uplinternal-miniheading>Current Incidents</uplinternal-miniheading>
|
|
|
|
|
${this.loading ? html`
|
|
|
|
|
<div class="loading-skeleton"></div>
|
2025-06-29 23:43:17 +00:00
|
|
|
` : this.currentIncidents.length ? html`
|
|
|
|
|
${this.currentIncidents.map(incident => this.renderIncident(incident, true))}
|
|
|
|
|
` :
|
2025-06-29 19:55:58 +00:00
|
|
|
html`<div class="noIncidentBox">No incidents ongoing.</div>`
|
|
|
|
|
}
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
<uplinternal-miniheading>Past Incidents</uplinternal-miniheading>
|
|
|
|
|
${this.loading ? html`
|
|
|
|
|
<div class="loading-skeleton"></div>
|
|
|
|
|
<div class="loading-skeleton"></div>
|
2025-12-24 10:12:29 +00:00
|
|
|
` : this.pastIncidents.length ?
|
2025-06-29 19:55:58 +00:00
|
|
|
this.pastIncidents.slice(0, 5).map(incident => this.renderIncident(incident, false)) :
|
|
|
|
|
html`<div class="noIncidentBox">No past incidents in the last ${this.daysToShow} days.</div>`
|
|
|
|
|
}
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
${this.pastIncidents.length > 5 && !this.loading ? html`
|
|
|
|
|
<div class="show-more">
|
|
|
|
|
<button class="show-more-button" @click=${this.handleShowMore}>
|
|
|
|
|
Show ${this.pastIncidents.length - 5} more incidents
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
` : ''}
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private renderIncident(incident: IIncidentDetails, isCurrent: boolean): TemplateResult {
|
|
|
|
|
const latestUpdate = incident.updates[incident.updates.length - 1];
|
2025-12-23 19:16:17 +00:00
|
|
|
const duration = incident.endTime ?
|
2025-06-29 19:55:58 +00:00
|
|
|
this.formatDuration(incident.endTime - incident.startTime) :
|
|
|
|
|
this.formatDuration(Date.now() - incident.startTime);
|
2025-12-23 19:16:17 +00:00
|
|
|
|
|
|
|
|
const isActive = isCurrent && latestUpdate?.status !== 'resolved';
|
2025-12-24 10:12:29 +00:00
|
|
|
const isExpanded = this.expandedIncidents.has(incident.id);
|
2025-12-23 19:16:17 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
return html`
|
2025-12-24 10:12:29 +00:00
|
|
|
<div class="incident-card ${isExpanded ? 'expanded' : ''} ${isActive ? 'active-incident' : ''}">
|
|
|
|
|
<div class="incident-header" @click=${() => this.toggleIncident(incident.id)}>
|
|
|
|
|
<div class="incident-severity ${incident.severity}"></div>
|
|
|
|
|
|
|
|
|
|
<div class="incident-main">
|
|
|
|
|
<div class="incident-title-row">
|
|
|
|
|
<h3 class="incident-title">${incident.title}</h3>
|
|
|
|
|
<span class="incident-status ${latestUpdate.status}">
|
|
|
|
|
<dees-icon .icon=${this.statusIcons[latestUpdate.status as TIncidentStatus]} .iconSize=${12}></dees-icon>
|
|
|
|
|
${this.statusLabels[latestUpdate.status as TIncidentStatus] || latestUpdate.status}
|
|
|
|
|
</span>
|
2025-06-29 23:43:17 +00:00
|
|
|
</div>
|
2025-12-24 10:12:29 +00:00
|
|
|
<div class="incident-meta">
|
|
|
|
|
<span class="incident-meta-item">
|
|
|
|
|
<dees-icon .icon=${'lucide:Calendar'} .iconSize=${12}></dees-icon>
|
|
|
|
|
${this.formatDate(incident.startTime)}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="incident-meta-item">
|
|
|
|
|
<dees-icon .icon=${'lucide:Clock'} .iconSize=${12}></dees-icon>
|
|
|
|
|
${duration}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="incident-meta-item">
|
|
|
|
|
<dees-icon .icon=${'lucide:Server'} .iconSize=${12}></dees-icon>
|
|
|
|
|
${incident.affectedServices.length} service${incident.affectedServices.length !== 1 ? 's' : ''}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="incident-meta-item">
|
|
|
|
|
<dees-icon .icon=${'lucide:MessageSquare'} .iconSize=${12}></dees-icon>
|
|
|
|
|
${incident.updates.length} update${incident.updates.length !== 1 ? 's' : ''}
|
|
|
|
|
</span>
|
2025-06-29 23:43:17 +00:00
|
|
|
</div>
|
2025-06-29 19:55:58 +00:00
|
|
|
</div>
|
2025-12-24 10:12:29 +00:00
|
|
|
|
|
|
|
|
<button class="incident-expand ${isExpanded ? 'expanded' : ''}">
|
|
|
|
|
<dees-icon .icon=${'lucide:ChevronDown'} .iconSize=${16}></dees-icon>
|
|
|
|
|
</button>
|
2025-06-29 19:55:58 +00:00
|
|
|
</div>
|
2025-12-24 10:12:29 +00:00
|
|
|
|
|
|
|
|
${isExpanded ? html`
|
2025-06-29 19:55:58 +00:00
|
|
|
<div class="incident-body">
|
2025-12-24 10:12:29 +00:00
|
|
|
${incident.impact ? html`
|
|
|
|
|
<div class="incident-impact">
|
|
|
|
|
<strong>Impact:</strong> ${incident.impact}
|
|
|
|
|
</div>
|
|
|
|
|
` : ''}
|
|
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
${incident.affectedServices.length > 0 ? html`
|
|
|
|
|
<div class="affected-services">
|
|
|
|
|
<div class="affected-services-title">Affected Services:</div>
|
|
|
|
|
${incident.affectedServices.map(service => html`
|
|
|
|
|
<span class="service-tag">${service}</span>
|
|
|
|
|
`)}
|
|
|
|
|
</div>
|
|
|
|
|
` : ''}
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
${incident.updates.length > 0 ? html`
|
|
|
|
|
<div class="incident-updates">
|
2025-12-23 19:16:17 +00:00
|
|
|
<h4 class="updates-title">Updates</h4>
|
|
|
|
|
<div class="timeline">
|
|
|
|
|
${incident.updates.slice(-3).reverse().map((update, index) => this.renderUpdate(update, index))}
|
|
|
|
|
</div>
|
2025-06-29 19:55:58 +00:00
|
|
|
</div>
|
|
|
|
|
` : ''}
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
${incident.rootCause && isCurrent === false ? html`
|
|
|
|
|
<div class="incident-impact" style="margin-top: 12px;">
|
|
|
|
|
<strong>Root Cause:</strong> ${incident.rootCause}
|
|
|
|
|
</div>
|
|
|
|
|
` : ''}
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
${incident.resolution && isCurrent === false ? html`
|
|
|
|
|
<div class="incident-impact" style="margin-top: 12px;">
|
|
|
|
|
<strong>Resolution:</strong> ${incident.resolution}
|
|
|
|
|
</div>
|
|
|
|
|
` : ''}
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-30 07:54:17 +00:00
|
|
|
<div class="incident-actions">
|
2025-12-24 10:12:29 +00:00
|
|
|
<button
|
2025-06-30 07:54:17 +00:00
|
|
|
class="subscribe-button ${this.isSubscribedToIncident(incident.id) ? 'subscribed' : ''}"
|
|
|
|
|
@click=${(e: Event) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
this.handleIncidentSubscribe(incident);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
${this.isSubscribedToIncident(incident.id) ? html`
|
2025-12-24 10:12:29 +00:00
|
|
|
<dees-icon .icon=${'lucide:Check'} .iconSize=${14}></dees-icon>
|
2025-06-30 07:54:17 +00:00
|
|
|
Subscribed to updates
|
|
|
|
|
` : html`
|
2025-12-24 10:12:29 +00:00
|
|
|
<dees-icon .icon=${'lucide:Bell'} .iconSize=${14}></dees-icon>
|
2025-06-30 07:54:17 +00:00
|
|
|
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>
|
2025-06-29 19:55:58 +00:00
|
|
|
</div>
|
2025-06-29 23:43:17 +00:00
|
|
|
` : ''}
|
2021-09-27 00:45:17 +02:00
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-23 19:16:17 +00:00
|
|
|
private renderUpdate(update: any, index: number = 0): TemplateResult {
|
2025-06-29 19:55:58 +00:00
|
|
|
return html`
|
2025-12-23 19:16:17 +00:00
|
|
|
<div class="update-item" style="animation-delay: ${index * 50}ms">
|
|
|
|
|
<div class="update-time">
|
|
|
|
|
${this.formatDate(update.timestamp)}
|
|
|
|
|
${update.status ? html`<span class="update-status-badge">${update.status}</span>` : ''}
|
|
|
|
|
</div>
|
2025-06-29 19:55:58 +00:00
|
|
|
<div class="update-message">${update.message}</div>
|
|
|
|
|
${update.author ? html`
|
2025-12-23 19:16:17 +00:00
|
|
|
<div class="update-author">
|
2025-12-24 10:12:29 +00:00
|
|
|
<dees-icon .icon=${'lucide:User'} .iconSize=${12}></dees-icon>
|
2025-12-23 19:16:17 +00:00
|
|
|
${update.author}
|
|
|
|
|
</div>
|
2025-06-29 19:55:58 +00:00
|
|
|
` : ''}
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private formatDate(timestamp: number): string {
|
|
|
|
|
const date = new Date(timestamp);
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
const diff = now - timestamp;
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
// Less than 1 hour ago
|
|
|
|
|
if (diff < 60 * 60 * 1000) {
|
|
|
|
|
const minutes = Math.floor(diff / (60 * 1000));
|
|
|
|
|
return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`;
|
|
|
|
|
}
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
// Less than 24 hours ago
|
|
|
|
|
if (diff < 24 * 60 * 60 * 1000) {
|
|
|
|
|
const hours = Math.floor(diff / (60 * 60 * 1000));
|
|
|
|
|
return `${hours} hour${hours !== 1 ? 's' : ''} ago`;
|
|
|
|
|
}
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
// Less than 7 days ago
|
|
|
|
|
if (diff < 7 * 24 * 60 * 60 * 1000) {
|
|
|
|
|
const days = Math.floor(diff / (24 * 60 * 60 * 1000));
|
|
|
|
|
return `${days} day${days !== 1 ? 's' : ''} ago`;
|
|
|
|
|
}
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
// Default to full date
|
2025-12-24 10:12:29 +00:00
|
|
|
return date.toLocaleDateString('en-US', {
|
|
|
|
|
month: 'short',
|
2025-06-29 19:55:58 +00:00
|
|
|
day: 'numeric',
|
|
|
|
|
year: date.getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private formatDuration(milliseconds: number): string {
|
|
|
|
|
const minutes = Math.floor(milliseconds / (60 * 1000));
|
|
|
|
|
const hours = Math.floor(minutes / 60);
|
|
|
|
|
const days = Math.floor(hours / 24);
|
2025-12-24 10:12:29 +00:00
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
if (days > 0) {
|
|
|
|
|
return `${days}d ${hours % 24}h`;
|
|
|
|
|
} else if (hours > 0) {
|
|
|
|
|
return `${hours}h ${minutes % 60}m`;
|
|
|
|
|
} else {
|
|
|
|
|
return `${minutes}m`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-29 23:43:17 +00:00
|
|
|
private toggleIncident(incidentId: string) {
|
|
|
|
|
const newExpanded = new Set(this.expandedIncidents);
|
|
|
|
|
if (newExpanded.has(incidentId)) {
|
|
|
|
|
newExpanded.delete(incidentId);
|
|
|
|
|
} else {
|
|
|
|
|
newExpanded.add(incidentId);
|
|
|
|
|
}
|
|
|
|
|
this.expandedIncidents = newExpanded;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-29 19:55:58 +00:00
|
|
|
private handleIncidentClick(incident: IIncidentDetails) {
|
|
|
|
|
this.dispatchEvent(new CustomEvent('incidentClick', {
|
|
|
|
|
detail: { incident },
|
|
|
|
|
bubbles: true,
|
|
|
|
|
composed: true
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private handleShowMore() {
|
|
|
|
|
// This would typically load more incidents or navigate to a full list
|
|
|
|
|
console.log('Show more incidents');
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-30 07:54:17 +00:00
|
|
|
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', {
|
2025-12-24 10:12:29 +00:00
|
|
|
detail: {
|
2025-06-30 07:54:17 +00:00
|
|
|
incident,
|
2025-12-24 10:12:29 +00:00
|
|
|
incidentId: incident.id
|
2025-06-30 07:54:17 +00:00
|
|
|
},
|
|
|
|
|
bubbles: true,
|
|
|
|
|
composed: true
|
|
|
|
|
}));
|
|
|
|
|
} else {
|
|
|
|
|
newSubscribed.add(incident.id);
|
|
|
|
|
this.dispatchEvent(new CustomEvent('incidentSubscribe', {
|
2025-12-24 10:12:29 +00:00
|
|
|
detail: {
|
2025-06-30 07:54:17 +00:00
|
|
|
incident,
|
|
|
|
|
incidentId: incident.id,
|
|
|
|
|
incidentTitle: incident.title,
|
|
|
|
|
affectedServices: incident.affectedServices
|
|
|
|
|
},
|
|
|
|
|
bubbles: true,
|
|
|
|
|
composed: true
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
this.subscribedIncidents = newSubscribed;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-27 00:45:17 +02:00
|
|
|
public dispatchReportNewIncident() {
|
2025-06-29 19:55:58 +00:00
|
|
|
this.dispatchEvent(new CustomEvent('reportNewIncident', {
|
|
|
|
|
bubbles: true,
|
|
|
|
|
composed: true
|
|
|
|
|
}));
|
2021-09-27 00:45:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public dispatchStatusSubscribe() {
|
2025-06-29 19:55:58 +00:00
|
|
|
this.dispatchEvent(new CustomEvent('statusSubscribe', {
|
|
|
|
|
bubbles: true,
|
|
|
|
|
composed: true
|
|
|
|
|
}));
|
2021-09-27 00:45:17 +02:00
|
|
|
}
|
2021-09-27 00:49:30 +02:00
|
|
|
}
|