feat(monitor): add extended monitor statuses, check configuration, status overrides/paused indicators, and incident update templates
This commit is contained in:
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-12-24 - 1.1.0 - feat(monitor)
|
||||||
|
add extended monitor statuses, check configuration, status overrides/paused indicators, and incident update templates
|
||||||
|
|
||||||
|
- Extend TStatusType with new statuses: initializing, error, paused.
|
||||||
|
- Add statusMode, manualStatus, paused, checkType, checkConfig and intervalMs to service and monitor interfaces.
|
||||||
|
- Update monitor list UI to show manual-override and paused indicators, new status badges, and include new statuses in status filter.
|
||||||
|
- Add quick templates to incident update form that prefill both status and message; update applyTemplate accordingly.
|
||||||
|
- Enhance monitor form to support checkType/ICheckConfig, statusMode selection, pause flag, interval options and additional validation (domain & PageRank search term).
|
||||||
|
- Add styles and icons for new statuses and status indicator badges.
|
||||||
|
|
||||||
## 2025-12-24 - 1.0.3 - fix(catalog_admin)
|
## 2025-12-24 - 1.0.3 - fix(catalog_admin)
|
||||||
no changes detected, no release required
|
no changes detected, no release required
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@uptime.link/statuspage-admin',
|
name: '@uptime.link/statuspage-admin',
|
||||||
version: '1.0.3',
|
version: '1.1.0',
|
||||||
description: 'Admin components for managing UptimeLink status pages, monitors, and incidents.'
|
description: 'Admin components for managing UptimeLink status pages, monitors, and incidents.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,11 +308,12 @@ export class UpladminIncidentUpdate extends DeesElement {
|
|||||||
{ value: 'postmortem', label: 'Postmortem', desc: 'Analysis complete' },
|
{ value: 'postmortem', label: 'Postmortem', desc: 'Analysis complete' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const templates: Array<{ icon: string; label: string; message: string }> = [
|
const templates: Array<{ icon: string; label: string; status: TIncidentStatus; message: string }> = [
|
||||||
{ icon: 'lucide:Search', label: 'Started investigating', message: 'We are currently investigating this issue.' },
|
{ icon: 'lucide:Search', label: 'Started investigating', status: 'investigating', message: 'We are currently investigating this issue.' },
|
||||||
{ icon: 'lucide:Target', label: 'Issue identified', message: 'We have identified the root cause and are working on a fix.' },
|
{ icon: 'lucide:Target', label: 'Issue identified', status: 'identified', message: 'We have identified the root cause and are working on a fix.' },
|
||||||
{ icon: 'lucide:Rocket', label: 'Fix deployed', message: 'A fix has been deployed. We are monitoring the results.' },
|
{ icon: 'lucide:Rocket', label: 'Fix deployed', status: 'monitoring', message: 'A fix has been deployed. We are monitoring the results.' },
|
||||||
{ icon: 'lucide:CheckCircle', label: 'Resolved', message: 'This incident has been resolved. All systems are operating normally.' },
|
{ icon: 'lucide:CheckCircle', label: 'Resolved', status: 'resolved', message: 'This incident has been resolved. All systems are operating normally.' },
|
||||||
|
{ icon: 'lucide:FileText', label: 'Postmortem', status: 'postmortem', message: 'Postmortem will be released shortly.' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const severityIcons: Record<string, string> = {
|
const severityIcons: Record<string, string> = {
|
||||||
@@ -340,6 +341,19 @@ export class UpladminIncidentUpdate extends DeesElement {
|
|||||||
|
|
||||||
<div class="update-body">
|
<div class="update-body">
|
||||||
<dees-form>
|
<dees-form>
|
||||||
|
<div class="template-section">
|
||||||
|
<label class="field-label">Quick Templates</label>
|
||||||
|
<div class="template-label">Select a template to prefill status and message:</div>
|
||||||
|
<div class="template-buttons">
|
||||||
|
${templates.map(tpl => html`
|
||||||
|
<button type="button" class="template-btn" @click="${() => this.applyTemplate(tpl)}">
|
||||||
|
<dees-icon .icon=${tpl.icon} .iconSize=${12}></dees-icon>
|
||||||
|
${tpl.label}
|
||||||
|
</button>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="field-label required">Status</label>
|
<label class="field-label required">Status</label>
|
||||||
<div class="status-grid">
|
<div class="status-grid">
|
||||||
@@ -364,17 +378,6 @@ export class UpladminIncidentUpdate extends DeesElement {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="field-label required">Update Message</label>
|
<label class="field-label required">Update Message</label>
|
||||||
<div class="template-section">
|
|
||||||
<div class="template-label">Quick templates:</div>
|
|
||||||
<div class="template-buttons">
|
|
||||||
${templates.map(tpl => html`
|
|
||||||
<button type="button" class="template-btn" @click="${() => this.applyTemplate(tpl.message)}">
|
|
||||||
<dees-icon .icon=${tpl.icon} .iconSize=${12}></dees-icon>
|
|
||||||
${tpl.label}
|
|
||||||
</button>
|
|
||||||
`)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<dees-input-text
|
<dees-input-text
|
||||||
key="message"
|
key="message"
|
||||||
inputType="textarea"
|
inputType="textarea"
|
||||||
@@ -430,8 +433,8 @@ export class UpladminIncidentUpdate extends DeesElement {
|
|||||||
this.formData = { ...this.formData, status };
|
this.formData = { ...this.formData, status };
|
||||||
}
|
}
|
||||||
|
|
||||||
private applyTemplate(message: string) {
|
private applyTemplate(template: { status: TIncidentStatus; message: string }) {
|
||||||
this.formData = { ...this.formData, message };
|
this.formData = { ...this.formData, status: template.status, message: template.message };
|
||||||
}
|
}
|
||||||
|
|
||||||
private validate(): boolean {
|
private validate(): boolean {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
state,
|
state,
|
||||||
} from '@design.estate/dees-element';
|
} from '@design.estate/dees-element';
|
||||||
import * as sharedStyles from '../../styles/shared.styles.js';
|
import * as sharedStyles from '../../styles/shared.styles.js';
|
||||||
import type { IMonitorFormData, IServiceStatus } from '../../interfaces/index.js';
|
import type { IMonitorFormData, IServiceStatus, ICheckConfig, TStatusType, TCheckType, TStatusMode } from '../../interfaces/index.js';
|
||||||
import { demoFunc } from './upladmin-monitor-form.demo.js';
|
import { demoFunc } from './upladmin-monitor-form.demo.js';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -20,8 +20,6 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type TStatusType = 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
|
|
||||||
|
|
||||||
@customElement('upladmin-monitor-form')
|
@customElement('upladmin-monitor-form')
|
||||||
export class UpladminMonitorForm extends DeesElement {
|
export class UpladminMonitorForm extends DeesElement {
|
||||||
public static demo = demoFunc;
|
public static demo = demoFunc;
|
||||||
@@ -45,80 +43,82 @@ export class UpladminMonitorForm extends DeesElement {
|
|||||||
description: '',
|
description: '',
|
||||||
category: '',
|
category: '',
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
currentStatus: 'operational',
|
statusMode: 'auto',
|
||||||
|
paused: false,
|
||||||
|
checkType: 'assumption',
|
||||||
|
checkConfig: { domain: '' },
|
||||||
|
intervalMs: 300000,
|
||||||
};
|
};
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
accessor errors: Record<string, string> = {};
|
accessor errors: Record<string, string> = {};
|
||||||
|
|
||||||
private statusIcons: Record<TStatusType, string> = {
|
private checkTypeLabels: Record<TCheckType, string> = {
|
||||||
operational: 'lucide:CheckCircle',
|
assumption: 'Assumption',
|
||||||
degraded: 'lucide:AlertTriangle',
|
function: 'Function',
|
||||||
partial_outage: 'lucide:AlertOctagon',
|
pwa: 'PWA',
|
||||||
major_outage: 'lucide:XCircle',
|
pagerank: 'PageRank',
|
||||||
maintenance: 'lucide:Wrench',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private intervalOptions = [
|
||||||
|
{ value: 60000, label: '1 min' },
|
||||||
|
{ value: 300000, label: '5 min' },
|
||||||
|
{ value: 900000, label: '15 min' },
|
||||||
|
{ value: 1800000, label: '30 min' },
|
||||||
|
{ value: 3600000, label: '1 hour' },
|
||||||
|
];
|
||||||
|
|
||||||
public static styles = [
|
public static styles = [
|
||||||
plugins.domtools.elementBasic.staticStyles,
|
plugins.domtools.elementBasic.staticStyles,
|
||||||
sharedStyles.commonStyles,
|
sharedStyles.commonStyles,
|
||||||
css`
|
css`
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-container {
|
.form-container {
|
||||||
background: ${sharedStyles.colors.background.secondary};
|
background: ${cssManager.bdTheme('#ffffff', '#09090b')};
|
||||||
border: 1px solid ${sharedStyles.colors.border.default};
|
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
||||||
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-header {
|
.form-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: ${unsafeCSS(sharedStyles.spacing.md)};
|
gap: 12px;
|
||||||
padding: ${unsafeCSS(sharedStyles.spacing.lg)};
|
padding: 20px 24px;
|
||||||
border-bottom: 1px solid ${sharedStyles.colors.border.default};
|
border-bottom: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
||||||
background: ${sharedStyles.colors.background.muted};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-header dees-icon {
|
.form-header dees-icon {
|
||||||
--icon-color: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
|
--icon-color: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-title-wrapper {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-title {
|
.form-title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: ${sharedStyles.colors.text.primary};
|
color: ${cssManager.bdTheme('#09090b', '#fafafa')};
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-subtitle {
|
.form-subtitle {
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
color: ${sharedStyles.colors.text.muted};
|
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
|
||||||
margin-top: 4px;
|
margin: 4px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-body {
|
.form-body {
|
||||||
display: grid;
|
padding: 24px;
|
||||||
gap: ${unsafeCSS(sharedStyles.spacing.lg)};
|
display: flex;
|
||||||
padding: ${unsafeCSS(sharedStyles.spacing.lg)};
|
flex-direction: column;
|
||||||
}
|
gap: 24px;
|
||||||
|
|
||||||
dees-form {
|
|
||||||
display: contents;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: ${unsafeCSS(sharedStyles.spacing.md)};
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
@@ -127,85 +127,58 @@ export class UpladminMonitorForm extends DeesElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: ${cssManager.bdTheme('#09090b', '#fafafa')};
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-box {
|
||||||
|
background: ${cssManager.bdTheme('#fafafa', '#18181b')};
|
||||||
|
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container {
|
||||||
|
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container dees-editor {
|
||||||
|
height: 180px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.form-actions {
|
.form-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
|
gap: 8px;
|
||||||
padding: ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.lg)};
|
padding: 16px 24px;
|
||||||
border-top: 1px solid ${sharedStyles.colors.border.default};
|
border-top: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
||||||
background: ${sharedStyles.colors.background.muted};
|
background: ${cssManager.bdTheme('#fafafa', '#18181b')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-section {
|
.search-engines {
|
||||||
margin-top: ${unsafeCSS(sharedStyles.spacing.sm)};
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-options {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
||||||
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-option {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
gap: 16px;
|
||||||
gap: 12px;
|
|
||||||
padding: 14px 16px;
|
|
||||||
background: ${sharedStyles.colors.background.primary};
|
|
||||||
border: 2px solid ${sharedStyles.colors.border.default};
|
|
||||||
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-option:hover {
|
|
||||||
border-color: ${sharedStyles.colors.border.strong};
|
|
||||||
background: ${sharedStyles.colors.background.muted};
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-option.selected {
|
|
||||||
border-color: ${sharedStyles.colors.accent.primary};
|
|
||||||
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.05)', 'rgba(96, 165, 250, 0.1)')};
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-option input {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-option dees-icon {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-option.operational dees-icon { --icon-color: ${sharedStyles.colors.status.operational}; }
|
|
||||||
.status-option.degraded dees-icon { --icon-color: ${sharedStyles.colors.status.degraded}; }
|
|
||||||
.status-option.partial_outage dees-icon { --icon-color: ${sharedStyles.colors.status.partialOutage}; }
|
|
||||||
.status-option.major_outage dees-icon { --icon-color: ${sharedStyles.colors.status.majorOutage}; }
|
|
||||||
.status-option.maintenance dees-icon { --icon-color: ${sharedStyles.colors.status.maintenance}; }
|
|
||||||
|
|
||||||
.status-label {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: ${sharedStyles.colors.text.primary};
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-label {
|
|
||||||
display: block;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: ${sharedStyles.colors.text.primary};
|
|
||||||
margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-label.required::after {
|
|
||||||
content: ' *';
|
|
||||||
color: ${sharedStyles.colors.accent.danger};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Style dees-input components */
|
|
||||||
dees-input-text,
|
|
||||||
dees-input-dropdown {
|
|
||||||
--dees-input-background: ${sharedStyles.colors.background.primary};
|
|
||||||
--dees-input-border-color: ${sharedStyles.colors.border.default};
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
];
|
];
|
||||||
@@ -225,113 +198,140 @@ export class UpladminMonitorForm extends DeesElement {
|
|||||||
|
|
||||||
public render(): TemplateResult {
|
public render(): TemplateResult {
|
||||||
const isEdit = !!this.monitor?.id;
|
const isEdit = !!this.monitor?.id;
|
||||||
const statusOptions: Array<{ value: TStatusType; label: string }> = [
|
|
||||||
{ value: 'operational', label: 'Operational' },
|
|
||||||
{ value: 'degraded', label: 'Degraded' },
|
|
||||||
{ value: 'partial_outage', label: 'Partial Outage' },
|
|
||||||
{ value: 'major_outage', label: 'Major Outage' },
|
|
||||||
{ value: 'maintenance', label: 'Maintenance' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const categoryOptions = this.categories.map(cat => ({ key: cat, option: cat, payload: null }));
|
const categoryOptions = this.categories.map(cat => ({ key: cat, option: cat, payload: null }));
|
||||||
const dependencyOptions = this.availableMonitors
|
const dependencyOptions = this.availableMonitors
|
||||||
.filter(m => m.id !== this.monitor?.id)
|
.filter(m => m.id !== this.monitor?.id)
|
||||||
.map(m => ({ key: m.id, option: m.displayName || m.name, payload: null }));
|
.map(m => ({ key: m.id, option: m.displayName || m.name, payload: null }));
|
||||||
|
const intervalOptions = this.intervalOptions.map(opt => ({ key: String(opt.value), option: opt.label, payload: null }));
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<div class="form-header">
|
<div class="form-header">
|
||||||
<dees-icon .icon=${isEdit ? 'lucide:Pencil' : 'lucide:Plus'} .iconSize=${24}></dees-icon>
|
<dees-icon .icon=${isEdit ? 'lucide:Pencil' : 'lucide:Plus'} .iconSize=${24}></dees-icon>
|
||||||
<div class="form-title-wrapper">
|
<div>
|
||||||
<h2 class="form-title">${isEdit ? 'Edit Monitor' : 'Create Monitor'}</h2>
|
<h2 class="form-title">${isEdit ? 'Edit Monitor' : 'Create Monitor'}</h2>
|
||||||
<p class="form-subtitle">
|
<p class="form-subtitle">${isEdit ? 'Update monitor configuration' : 'Add a new service to monitor'}</p>
|
||||||
${isEdit ? 'Update the monitor configuration' : 'Add a new service to monitor'}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-body">
|
<div class="form-body">
|
||||||
<dees-form>
|
<dees-form>
|
||||||
|
<!-- Basic Info -->
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<dees-input-text
|
<dees-input-text
|
||||||
key="name"
|
.label=${'Internal Name'}
|
||||||
label="Internal Name"
|
.value=${this.formData.name}
|
||||||
.value="${this.formData.name}"
|
.placeholder=${'api-server'}
|
||||||
placeholder="api-server"
|
.required=${true}
|
||||||
required
|
.description=${'Lowercase, no spaces'}
|
||||||
description="Lowercase, no spaces. Used as identifier."
|
@changeSubject=${(e: CustomEvent) => this.updateField('name', e.detail.value)}
|
||||||
@changeSubject="${this.handleNameChange}"
|
|
||||||
></dees-input-text>
|
></dees-input-text>
|
||||||
|
|
||||||
<dees-input-text
|
<dees-input-text
|
||||||
key="displayName"
|
.label=${'Display Name'}
|
||||||
label="Display Name"
|
.value=${this.formData.displayName}
|
||||||
.value="${this.formData.displayName}"
|
.placeholder=${'API Server'}
|
||||||
placeholder="API Server"
|
.required=${true}
|
||||||
required
|
.description=${'Human-readable name'}
|
||||||
description="Human-readable name shown to users."
|
@changeSubject=${(e: CustomEvent) => this.updateField('displayName', e.detail.value)}
|
||||||
@changeSubject="${this.handleDisplayNameChange}"
|
|
||||||
></dees-input-text>
|
></dees-input-text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<dees-input-text
|
<dees-input-text
|
||||||
key="description"
|
.label=${'Description'}
|
||||||
label="Description"
|
.inputType=${'textarea'}
|
||||||
inputType="textarea"
|
.value=${this.formData.description || ''}
|
||||||
.value="${this.formData.description || ''}"
|
.placeholder=${'Brief description of this service...'}
|
||||||
placeholder="Brief description of what this service does..."
|
@changeSubject=${(e: CustomEvent) => this.updateField('description', e.detail.value)}
|
||||||
@changeSubject="${this.handleDescriptionChange}"
|
|
||||||
></dees-input-text>
|
></dees-input-text>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<dees-input-dropdown
|
<dees-input-dropdown
|
||||||
key="category"
|
.label=${'Category'}
|
||||||
label="Category"
|
.options=${categoryOptions}
|
||||||
.options="${categoryOptions}"
|
.selectedOption=${this.formData.category || ''}
|
||||||
.selectedOption="${this.formData.category || ''}"
|
@selectedOption=${(e: CustomEvent) => this.updateField('category', e.detail)}
|
||||||
placeholder="Select category..."
|
|
||||||
@selectedOption="${this.handleCategoryChange}"
|
|
||||||
></dees-input-dropdown>
|
></dees-input-dropdown>
|
||||||
|
|
||||||
<dees-input-dropdown
|
<dees-input-dropdown
|
||||||
key="dependencies"
|
.label=${'Dependencies'}
|
||||||
label="Dependencies"
|
.options=${dependencyOptions}
|
||||||
.options="${dependencyOptions}"
|
.selectedOptions=${this.formData.dependencies || []}
|
||||||
.selectedOptions="${this.formData.dependencies || []}"
|
.multiple=${true}
|
||||||
multiple
|
@selectedOption=${(e: CustomEvent) => this.updateField('dependencies', Array.isArray(e.detail) ? e.detail : [e.detail])}
|
||||||
description="Services this monitor depends on."
|
|
||||||
@selectedOption="${this.handleDependenciesChange}"
|
|
||||||
></dees-input-dropdown>
|
></dees-input-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="status-section">
|
<!-- Check Type -->
|
||||||
<label class="field-label required">Current Status</label>
|
<div class="form-section">
|
||||||
<div class="status-options">
|
<dees-input-multitoggle
|
||||||
${statusOptions.map(opt => html`
|
.label=${'Check Type'}
|
||||||
<label
|
.options=${Object.values(this.checkTypeLabels)}
|
||||||
class="status-option ${opt.value} ${this.formData.currentStatus === opt.value ? 'selected' : ''}"
|
.selectedOption=${this.checkTypeLabels[this.formData.checkType]}
|
||||||
@click="${() => this.handleStatusChange(opt.value)}"
|
@changeSubject=${(e: CustomEvent) => this.handleCheckTypeChange(e.detail.selectedOption)}
|
||||||
>
|
></dees-input-multitoggle>
|
||||||
<input
|
</div>
|
||||||
type="radio"
|
|
||||||
name="currentStatus"
|
<!-- Check Configuration -->
|
||||||
value="${opt.value}"
|
<div class="form-section">
|
||||||
?checked="${this.formData.currentStatus === opt.value}"
|
<div class="config-box">
|
||||||
/>
|
${this.renderCheckConfigFields()}
|
||||||
<dees-icon .icon=${this.statusIcons[opt.value]} .iconSize=${20}></dees-icon>
|
|
||||||
<span class="status-label">${opt.label}</span>
|
|
||||||
</label>
|
|
||||||
`)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Interval -->
|
||||||
|
<dees-input-dropdown
|
||||||
|
.label=${'Check Interval'}
|
||||||
|
.options=${intervalOptions}
|
||||||
|
.selectedOption=${String(this.formData.intervalMs)}
|
||||||
|
@selectedOption=${(e: CustomEvent) => this.updateField('intervalMs', parseInt(e.detail))}
|
||||||
|
></dees-input-dropdown>
|
||||||
|
|
||||||
|
<!-- Pause Toggle -->
|
||||||
|
<dees-input-checkbox
|
||||||
|
.label=${'Pause Monitor'}
|
||||||
|
.description=${'When paused, status will show as "paused" and checks won\'t run'}
|
||||||
|
.value=${this.formData.paused}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateField('paused', e.detail.value)}
|
||||||
|
></dees-input-checkbox>
|
||||||
|
|
||||||
|
<!-- Status Override (Edit mode only) -->
|
||||||
|
${isEdit ? html`
|
||||||
|
<div class="form-section">
|
||||||
|
<dees-input-multitoggle
|
||||||
|
.label=${'Status Mode'}
|
||||||
|
.description=${'Auto uses check results, Manual lets you override'}
|
||||||
|
.options=${['Auto', 'Manual']}
|
||||||
|
.selectedOption=${this.formData.statusMode === 'auto' ? 'Auto' : 'Manual'}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.handleStatusModeChange(e.detail.selectedOption)}
|
||||||
|
></dees-input-multitoggle>
|
||||||
|
|
||||||
|
${this.formData.statusMode === 'manual' ? html`
|
||||||
|
<dees-input-radiogroup
|
||||||
|
.label=${'Manual Status'}
|
||||||
|
.options=${[
|
||||||
|
{ key: 'operational', option: 'Operational' },
|
||||||
|
{ key: 'degraded', option: 'Degraded' },
|
||||||
|
{ key: 'partial_outage', option: 'Partial Outage' },
|
||||||
|
{ key: 'major_outage', option: 'Major Outage' },
|
||||||
|
{ key: 'maintenance', option: 'Maintenance' },
|
||||||
|
]}
|
||||||
|
.selectedOption=${this.formData.manualStatus || 'operational'}
|
||||||
|
.direction=${'horizontal'}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateField('manualStatus', e.detail.selectedOption)}
|
||||||
|
></dees-input-radiogroup>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
</dees-form>
|
</dees-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<dees-button type="discreet" @click="${this.handleCancel}" ?disabled="${this.loading}">
|
<dees-button .type=${'discreet'} @click=${this.handleCancel} ?disabled=${this.loading}>
|
||||||
Cancel
|
Cancel
|
||||||
</dees-button>
|
</dees-button>
|
||||||
<dees-button type="highlighted" @click="${this.handleSave}" ?disabled="${this.loading}">
|
<dees-button .type=${'highlighted'} @click=${this.handleSave} ?disabled=${this.loading}>
|
||||||
${this.loading ? html`<dees-spinner .size=${16}></dees-spinner>` : ''}
|
${this.loading ? html`<dees-spinner .size=${16}></dees-spinner>` : ''}
|
||||||
${isEdit ? 'Update Monitor' : 'Create Monitor'}
|
${isEdit ? 'Update Monitor' : 'Create Monitor'}
|
||||||
</dees-button>
|
</dees-button>
|
||||||
@@ -340,40 +340,168 @@ export class UpladminMonitorForm extends DeesElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleNameChange(e: CustomEvent) {
|
private renderCheckConfigFields(): TemplateResult {
|
||||||
this.formData = { ...this.formData, name: e.detail };
|
const config = this.formData.checkConfig;
|
||||||
if (this.errors.name) {
|
|
||||||
this.errors = { ...this.errors, name: '' };
|
switch (this.formData.checkType) {
|
||||||
|
case 'assumption':
|
||||||
|
return html`
|
||||||
|
<dees-input-text
|
||||||
|
.label=${'Domain'}
|
||||||
|
.value=${config.domain || ''}
|
||||||
|
.placeholder=${'api.example.com'}
|
||||||
|
.required=${true}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('domain', e.detail.value)}
|
||||||
|
></dees-input-text>
|
||||||
|
<div class="form-row">
|
||||||
|
<dees-input-text
|
||||||
|
.label=${'Expected Status Code'}
|
||||||
|
.value=${config.expectedStatusCode || ''}
|
||||||
|
.placeholder=${'200'}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('expectedStatusCode', e.detail.value)}
|
||||||
|
></dees-input-text>
|
||||||
|
<dees-input-text
|
||||||
|
.label=${'Expected Title'}
|
||||||
|
.value=${config.expectedTitle || ''}
|
||||||
|
.placeholder=${'Page Title'}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('expectedTitle', e.detail.value)}
|
||||||
|
></dees-input-text>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
case 'function':
|
||||||
|
return html`
|
||||||
|
<dees-input-text
|
||||||
|
.label=${'Domain'}
|
||||||
|
.value=${config.domain || ''}
|
||||||
|
.placeholder=${'api.example.com'}
|
||||||
|
.required=${true}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('domain', e.detail.value)}
|
||||||
|
></dees-input-text>
|
||||||
|
<div>
|
||||||
|
<p class="section-label">Function Definition</p>
|
||||||
|
<div class="editor-container">
|
||||||
|
<dees-editor
|
||||||
|
.value=${config.functionDef || `async (context: { domain: string }) => {
|
||||||
|
const response = await fetch(\`https://\${context.domain}\`);
|
||||||
|
return response.status === 200;
|
||||||
|
}`}
|
||||||
|
.language=${'typescript'}
|
||||||
|
.options=${{
|
||||||
|
lineNumbers: 'on',
|
||||||
|
minimap: { enabled: false },
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
fontSize: 13,
|
||||||
|
tabSize: 2,
|
||||||
|
}}
|
||||||
|
@change=${(e: CustomEvent) => this.updateCheckConfig('functionDef', e.detail)}
|
||||||
|
></dees-editor>
|
||||||
|
</div>
|
||||||
|
<p class="editor-hint">Return true for success, false for failure</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
case 'pwa':
|
||||||
|
return html`
|
||||||
|
<dees-input-text
|
||||||
|
.label=${'Domain'}
|
||||||
|
.value=${config.domain || ''}
|
||||||
|
.placeholder=${'example.com'}
|
||||||
|
.required=${true}
|
||||||
|
.description=${'Domain to run Lighthouse PWA analysis on'}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('domain', e.detail.value)}
|
||||||
|
></dees-input-text>
|
||||||
|
`;
|
||||||
|
|
||||||
|
case 'pagerank':
|
||||||
|
return html`
|
||||||
|
<div class="form-row">
|
||||||
|
<dees-input-text
|
||||||
|
.label=${'Domain'}
|
||||||
|
.value=${config.domain || ''}
|
||||||
|
.placeholder=${'example.com'}
|
||||||
|
.required=${true}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('domain', e.detail.value)}
|
||||||
|
></dees-input-text>
|
||||||
|
<dees-input-text
|
||||||
|
.label=${'Search Term'}
|
||||||
|
.value=${config.searchTerm || ''}
|
||||||
|
.placeholder=${'your brand name'}
|
||||||
|
.required=${true}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('searchTerm', e.detail.value)}
|
||||||
|
></dees-input-text>
|
||||||
|
</div>
|
||||||
|
<div class="search-engines">
|
||||||
|
<dees-input-checkbox
|
||||||
|
.label=${'Google'}
|
||||||
|
.value=${config.checkGoogle !== false}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('checkGoogle', e.detail.value)}
|
||||||
|
></dees-input-checkbox>
|
||||||
|
<dees-input-checkbox
|
||||||
|
.label=${'Bing'}
|
||||||
|
.value=${config.checkBing === true}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('checkBing', e.detail.value)}
|
||||||
|
></dees-input-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
${config.checkGoogle !== false ? html`
|
||||||
|
<dees-input-text
|
||||||
|
.label=${'Google Min Rank'}
|
||||||
|
.value=${config.googleMinRank?.toString() || ''}
|
||||||
|
.placeholder=${'10'}
|
||||||
|
.description=${'Alert if rank drops below this'}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('googleMinRank', parseInt(e.detail.value) || undefined)}
|
||||||
|
></dees-input-text>
|
||||||
|
` : ''}
|
||||||
|
${config.checkBing ? html`
|
||||||
|
<dees-input-text
|
||||||
|
.label=${'Bing Min Rank'}
|
||||||
|
.value=${config.bingMinRank?.toString() || ''}
|
||||||
|
.placeholder=${'10'}
|
||||||
|
.description=${'Alert if rank drops below this'}
|
||||||
|
@changeSubject=${(e: CustomEvent) => this.updateCheckConfig('bingMinRank', parseInt(e.detail.value) || undefined)}
|
||||||
|
></dees-input-text>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return html``;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleDisplayNameChange(e: CustomEvent) {
|
private updateField(field: keyof IMonitorFormData, value: any) {
|
||||||
this.formData = { ...this.formData, displayName: e.detail };
|
this.formData = { ...this.formData, [field]: value };
|
||||||
if (this.errors.displayName) {
|
if (this.errors[field]) {
|
||||||
this.errors = { ...this.errors, displayName: '' };
|
this.errors = { ...this.errors, [field]: '' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleDescriptionChange(e: CustomEvent) {
|
private updateCheckConfig(field: keyof ICheckConfig, value: any) {
|
||||||
this.formData = { ...this.formData, description: e.detail };
|
this.formData = {
|
||||||
|
...this.formData,
|
||||||
|
checkConfig: { ...this.formData.checkConfig, [field]: value },
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleCategoryChange(e: CustomEvent) {
|
private handleCheckTypeChange(label: string) {
|
||||||
this.formData = { ...this.formData, category: e.detail };
|
const checkType = (Object.keys(this.checkTypeLabels) as TCheckType[])
|
||||||
|
.find(key => this.checkTypeLabels[key] === label) || 'assumption';
|
||||||
|
const domain = this.formData.checkConfig?.domain || '';
|
||||||
|
this.formData = {
|
||||||
|
...this.formData,
|
||||||
|
checkType,
|
||||||
|
checkConfig: { domain },
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleDependenciesChange(e: CustomEvent) {
|
private handleStatusModeChange(label: string) {
|
||||||
const selected = e.detail;
|
const mode: TStatusMode = label === 'Auto' ? 'auto' : 'manual';
|
||||||
if (Array.isArray(selected)) {
|
this.formData = {
|
||||||
this.formData = { ...this.formData, dependencies: selected };
|
...this.formData,
|
||||||
} else if (selected) {
|
statusMode: mode,
|
||||||
// Single selection mode, convert to array
|
manualStatus: mode === 'manual' && !this.formData.manualStatus ? 'operational' : this.formData.manualStatus,
|
||||||
this.formData = { ...this.formData, dependencies: [selected] };
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleStatusChange(status: TStatusType) {
|
|
||||||
this.formData = { ...this.formData, currentStatus: status };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private validate(): boolean {
|
private validate(): boolean {
|
||||||
@@ -389,6 +517,14 @@ export class UpladminMonitorForm extends DeesElement {
|
|||||||
errors.displayName = 'Display name is required';
|
errors.displayName = 'Display name is required';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.formData.checkConfig?.domain?.trim()) {
|
||||||
|
errors.domain = 'Domain is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.formData.checkType === 'pagerank' && !this.formData.checkConfig?.searchTerm?.trim()) {
|
||||||
|
errors.searchTerm = 'Search term is required for PageRank checks';
|
||||||
|
}
|
||||||
|
|
||||||
this.errors = errors;
|
this.errors = errors;
|
||||||
return Object.keys(errors).length === 0;
|
return Object.keys(errors).length === 0;
|
||||||
}
|
}
|
||||||
@@ -419,7 +555,11 @@ export class UpladminMonitorForm extends DeesElement {
|
|||||||
description: '',
|
description: '',
|
||||||
category: '',
|
category: '',
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
currentStatus: 'operational',
|
statusMode: 'auto',
|
||||||
|
paused: false,
|
||||||
|
checkType: 'assumption',
|
||||||
|
checkConfig: { domain: '' },
|
||||||
|
intervalMs: 300000,
|
||||||
};
|
};
|
||||||
this.errors = {};
|
this.errors = {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
state,
|
state,
|
||||||
} from '@design.estate/dees-element';
|
} from '@design.estate/dees-element';
|
||||||
import * as sharedStyles from '../../styles/shared.styles.js';
|
import * as sharedStyles from '../../styles/shared.styles.js';
|
||||||
import type { IServiceStatus } from '../../interfaces/index.js';
|
import type { IServiceStatus, TStatusType } from '../../interfaces/index.js';
|
||||||
import { demoFunc } from './upladmin-monitor-list.demo.js';
|
import { demoFunc } from './upladmin-monitor-list.demo.js';
|
||||||
import type { Column, ITableAction, DeesTable } from '@design.estate/dees-catalog';
|
import type { Column, ITableAction, DeesTable } from '@design.estate/dees-catalog';
|
||||||
|
|
||||||
@@ -21,8 +21,6 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type TStatusType = 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
|
|
||||||
|
|
||||||
@customElement('upladmin-monitor-list')
|
@customElement('upladmin-monitor-list')
|
||||||
export class UpladminMonitorList extends DeesElement {
|
export class UpladminMonitorList extends DeesElement {
|
||||||
public static demo = demoFunc;
|
public static demo = demoFunc;
|
||||||
@@ -45,6 +43,9 @@ export class UpladminMonitorList extends DeesElement {
|
|||||||
partial_outage: 'lucide:AlertOctagon',
|
partial_outage: 'lucide:AlertOctagon',
|
||||||
major_outage: 'lucide:XCircle',
|
major_outage: 'lucide:XCircle',
|
||||||
maintenance: 'lucide:Wrench',
|
maintenance: 'lucide:Wrench',
|
||||||
|
initializing: 'lucide:Loader',
|
||||||
|
error: 'lucide:AlertCircle',
|
||||||
|
paused: 'lucide:PauseCircle',
|
||||||
};
|
};
|
||||||
|
|
||||||
private statusLabels: Record<TStatusType, string> = {
|
private statusLabels: Record<TStatusType, string> = {
|
||||||
@@ -53,6 +54,9 @@ export class UpladminMonitorList extends DeesElement {
|
|||||||
partial_outage: 'Partial Outage',
|
partial_outage: 'Partial Outage',
|
||||||
major_outage: 'Major Outage',
|
major_outage: 'Major Outage',
|
||||||
maintenance: 'Maintenance',
|
maintenance: 'Maintenance',
|
||||||
|
initializing: 'Initializing',
|
||||||
|
error: 'Monitor Error',
|
||||||
|
paused: 'Paused',
|
||||||
};
|
};
|
||||||
|
|
||||||
public static styles = [
|
public static styles = [
|
||||||
@@ -183,6 +187,51 @@ export class UpladminMonitorList extends DeesElement {
|
|||||||
--icon-color: ${sharedStyles.colors.status.maintenance};
|
--icon-color: ${sharedStyles.colors.status.maintenance};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-badge.initializing {
|
||||||
|
background: ${cssManager.bdTheme('rgba(107, 114, 128, 0.1)', 'rgba(107, 114, 128, 0.15)')};
|
||||||
|
color: #6b7280;
|
||||||
|
--icon-color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.error {
|
||||||
|
background: ${cssManager.bdTheme('rgba(220, 38, 38, 0.1)', 'rgba(220, 38, 38, 0.15)')};
|
||||||
|
color: #dc2626;
|
||||||
|
--icon-color: #dc2626;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.paused {
|
||||||
|
background: ${cssManager.bdTheme('rgba(139, 92, 246, 0.1)', 'rgba(139, 92, 246, 0.15)')};
|
||||||
|
color: #8b5cf6;
|
||||||
|
--icon-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status indicators for override and pause */
|
||||||
|
.status-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator.override {
|
||||||
|
background: ${cssManager.bdTheme('rgba(234, 179, 8, 0.15)', 'rgba(234, 179, 8, 0.2)')};
|
||||||
|
--icon-color: #eab308;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator.paused {
|
||||||
|
background: ${cssManager.bdTheme('rgba(139, 92, 246, 0.15)', 'rgba(139, 92, 246, 0.2)')};
|
||||||
|
--icon-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
.monitor-info {
|
.monitor-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -316,10 +365,22 @@ export class UpladminMonitorList extends DeesElement {
|
|||||||
header: 'Status',
|
header: 'Status',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
renderer: (value, item) => html`
|
renderer: (value, item) => html`
|
||||||
<span class="status-badge ${item.currentStatus}">
|
<div class="status-cell">
|
||||||
<dees-icon .icon=${this.statusIcons[item.currentStatus]} .iconSize=${14}></dees-icon>
|
<span class="status-badge ${item.currentStatus}">
|
||||||
${this.statusLabels[item.currentStatus]}
|
<dees-icon .icon=${this.statusIcons[item.currentStatus]} .iconSize=${14}></dees-icon>
|
||||||
</span>
|
${this.statusLabels[item.currentStatus]}
|
||||||
|
</span>
|
||||||
|
${item.statusMode === 'manual' ? html`
|
||||||
|
<span class="status-indicator override" title="Manual Override">
|
||||||
|
<dees-icon .icon=${'lucide:AlertTriangle'} .iconSize=${12}></dees-icon>
|
||||||
|
</span>
|
||||||
|
` : ''}
|
||||||
|
${item.paused && item.currentStatus !== 'paused' ? html`
|
||||||
|
<span class="status-indicator paused" title="Execution Paused">
|
||||||
|
<dees-icon .icon=${'lucide:Pause'} .iconSize=${12}></dees-icon>
|
||||||
|
</span>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -391,6 +452,9 @@ export class UpladminMonitorList extends DeesElement {
|
|||||||
<option value="partial_outage" ?selected="${this.statusFilter === 'partial_outage'}">Partial Outage</option>
|
<option value="partial_outage" ?selected="${this.statusFilter === 'partial_outage'}">Partial Outage</option>
|
||||||
<option value="major_outage" ?selected="${this.statusFilter === 'major_outage'}">Major Outage</option>
|
<option value="major_outage" ?selected="${this.statusFilter === 'major_outage'}">Major Outage</option>
|
||||||
<option value="maintenance" ?selected="${this.statusFilter === 'maintenance'}">Maintenance</option>
|
<option value="maintenance" ?selected="${this.statusFilter === 'maintenance'}">Maintenance</option>
|
||||||
|
<option value="paused" ?selected="${this.statusFilter === 'paused'}">Paused</option>
|
||||||
|
<option value="initializing" ?selected="${this.statusFilter === 'initializing'}">Initializing</option>
|
||||||
|
<option value="error" ?selected="${this.statusFilter === 'error'}">Monitor Error</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
${this.categories.length > 0 ? html`
|
${this.categories.length > 0 ? html`
|
||||||
|
|||||||
@@ -1,10 +1,33 @@
|
|||||||
// Re-export interfaces from the public catalog for consistency
|
// Re-export interfaces from the public catalog for consistency
|
||||||
|
|
||||||
|
// Status types
|
||||||
|
export type TStatusType = 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance' | 'initializing' | 'error' | 'paused';
|
||||||
|
export type TCheckType = 'assumption' | 'function' | 'pwa' | 'pagerank';
|
||||||
|
export type TStatusMode = 'auto' | 'manual';
|
||||||
|
|
||||||
|
// Check configuration interface
|
||||||
|
export interface ICheckConfig {
|
||||||
|
domain: string;
|
||||||
|
// Assumption check fields
|
||||||
|
expectedTitle?: string;
|
||||||
|
expectedStatusCode?: string;
|
||||||
|
expectedDescription?: string;
|
||||||
|
// Function check fields
|
||||||
|
functionDef?: string;
|
||||||
|
// PageRank check fields
|
||||||
|
searchTerm?: string;
|
||||||
|
checkBing?: boolean;
|
||||||
|
checkGoogle?: boolean;
|
||||||
|
bingMinRank?: number;
|
||||||
|
googleMinRank?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IServiceStatus {
|
export interface IServiceStatus {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
currentStatus: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
|
currentStatus: TStatusType;
|
||||||
lastChecked: number;
|
lastChecked: number;
|
||||||
uptime30d: number;
|
uptime30d: number;
|
||||||
uptime90d: number;
|
uptime90d: number;
|
||||||
@@ -12,11 +35,19 @@ export interface IServiceStatus {
|
|||||||
category?: string;
|
category?: string;
|
||||||
dependencies?: string[];
|
dependencies?: string[];
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
|
// Status management
|
||||||
|
statusMode?: TStatusMode;
|
||||||
|
manualStatus?: TStatusType;
|
||||||
|
paused?: boolean;
|
||||||
|
// Check configuration
|
||||||
|
checkType?: TCheckType;
|
||||||
|
checkConfig?: ICheckConfig;
|
||||||
|
intervalMs?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IStatusHistoryPoint {
|
export interface IStatusHistoryPoint {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
status: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
|
status: TStatusType;
|
||||||
responseTime?: number;
|
responseTime?: number;
|
||||||
errorRate?: number;
|
errorRate?: number;
|
||||||
}
|
}
|
||||||
@@ -44,7 +75,7 @@ export interface IIncidentDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IOverallStatus {
|
export interface IOverallStatus {
|
||||||
status: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
|
status: TStatusType;
|
||||||
message: string;
|
message: string;
|
||||||
lastUpdated: number;
|
lastUpdated: number;
|
||||||
affectedServices: number;
|
affectedServices: number;
|
||||||
@@ -77,7 +108,14 @@ export interface IMonitorFormData {
|
|||||||
description?: string;
|
description?: string;
|
||||||
category?: string;
|
category?: string;
|
||||||
dependencies?: string[];
|
dependencies?: string[];
|
||||||
currentStatus: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
|
// Status management
|
||||||
|
statusMode: TStatusMode;
|
||||||
|
manualStatus?: TStatusType;
|
||||||
|
paused: boolean;
|
||||||
|
// Check configuration
|
||||||
|
checkType: TCheckType;
|
||||||
|
checkConfig: ICheckConfig;
|
||||||
|
intervalMs: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IIncidentFormData {
|
export interface IIncidentFormData {
|
||||||
|
|||||||
Reference in New Issue
Block a user