fix: update styles in various components to use dynamic theming and improve layout consistency

This commit is contained in:
Juergen Kunz
2025-06-19 12:14:52 +00:00
parent 2ab2e30336
commit 3ba47f9a71
9 changed files with 333 additions and 239 deletions

View File

@ -12,7 +12,8 @@
"test": "(tstest test/ --logfile --timeout 60)",
"start": "(node --max_old_space_size=250 ./cli.js)",
"startTs": "(node cli.ts.js)",
"build": "(tsbuild tsfolders --allowimplicitany && tsbundle website --production)"
"build": "(tsbuild tsfolders --allowimplicitany && npm run bundle)",
"bundle": "(tsbundle website --production)"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.6.4",
@ -29,7 +30,7 @@
"@api.global/typedserver": "^3.0.74",
"@api.global/typedsocket": "^3.0.0",
"@apiclient.xyz/cloudflare": "^6.4.1",
"@design.estate/dees-catalog": "^1.8.8",
"@design.estate/dees-catalog": "^1.8.13",
"@design.estate/dees-element": "^2.0.42",
"@push.rocks/projectinfo": "^5.0.1",
"@push.rocks/qenv": "^6.1.0",

10
pnpm-lock.yaml generated
View File

@ -24,8 +24,8 @@ importers:
specifier: ^6.4.1
version: 6.4.1
'@design.estate/dees-catalog':
specifier: ^1.8.8
version: 1.8.8
specifier: ^1.8.13
version: 1.8.13
'@design.estate/dees-element':
specifier: ^2.0.42
version: 2.0.42
@ -344,8 +344,8 @@ packages:
'@dabh/diagnostics@2.0.3':
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
'@design.estate/dees-catalog@1.8.8':
resolution: {integrity: sha512-so0Jju95vZGsNUKi/ze1bHoK1LOgkCuU+xeiWDMfUpGkJA8OE9wPJmy5hSyIecc352JtlhOkgmTh/XxFTGpkQg==}
'@design.estate/dees-catalog@1.8.13':
resolution: {integrity: sha512-ZsGaioZZlo4Z12VIVeE5KF1BrwDbDfiqI7DCr0Cl0wuQtZdHtrGbvbevQTxTXj9c5FlzU450eAs23sMAeRreJg==}
'@design.estate/dees-comms@1.0.27':
resolution: {integrity: sha512-GvzTUwkV442LD60T08iqSoqvhA02Mou5lFvvqBPc4yBUiU7cZISqBx+76xvMgMIEI9Dx9JfTl4/2nW8MoVAanw==}
@ -5087,7 +5087,7 @@ snapshots:
enabled: 2.0.0
kuler: 2.0.0
'@design.estate/dees-catalog@1.8.8':
'@design.estate/dees-catalog@1.8.13':
dependencies:
'@design.estate/dees-domtools': 2.3.2
'@design.estate/dees-element': 2.0.42

View File

@ -41,17 +41,17 @@ export class OpsViewConfig extends DeesElement {
shared.viewHostCss,
css`
.configSection {
background: white;
border: 1px solid #e9ecef;
background: ${cssManager.bdTheme('#fff', '#222')};
border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
border-radius: 8px;
margin-bottom: 24px;
overflow: hidden;
}
.sectionHeader {
background: #f8f9fa;
background: ${cssManager.bdTheme('#f8f9fa', '#1a1a1a')};
padding: 16px 24px;
border-bottom: 1px solid #e9ecef;
border-bottom: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
display: flex;
justify-content: space-between;
align-items: center;
@ -60,7 +60,7 @@ export class OpsViewConfig extends DeesElement {
.sectionTitle {
font-size: 18px;
font-weight: 600;
color: #333;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.sectionContent {
@ -74,7 +74,7 @@ export class OpsViewConfig extends DeesElement {
.fieldLabel {
font-size: 14px;
font-weight: 600;
color: #666;
color: ${cssManager.bdTheme('#666', '#999')};
margin-bottom: 8px;
display: block;
}
@ -82,11 +82,11 @@ export class OpsViewConfig extends DeesElement {
.fieldValue {
font-family: 'Consolas', 'Monaco', monospace;
font-size: 14px;
color: #333;
background: #f8f9fa;
color: ${cssManager.bdTheme('#333', '#ccc')};
background: ${cssManager.bdTheme('#f8f9fa', '#1a1a1a')};
padding: 8px 12px;
border-radius: 4px;
border: 1px solid #e9ecef;
border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
}
.configEditor {
@ -95,9 +95,9 @@ export class OpsViewConfig extends DeesElement {
font-family: 'Consolas', 'Monaco', monospace;
font-size: 14px;
padding: 12px;
border: 1px solid #e9ecef;
border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
border-radius: 4px;
background: #f8f9fa;
background: ${cssManager.bdTheme('#f8f9fa', '#1a1a1a')};
resize: vertical;
}
@ -108,30 +108,30 @@ export class OpsViewConfig extends DeesElement {
}
.warning {
background: #fff3cd;
border: 1px solid #ffeaa7;
background: ${cssManager.bdTheme('#fff3cd', '#4a4a1a')};
border: 1px solid ${cssManager.bdTheme('#ffeaa7', '#666633')};
border-radius: 4px;
padding: 12px;
margin-bottom: 16px;
color: #856404;
color: ${cssManager.bdTheme('#856404', '#ffcc66')};
display: flex;
align-items: center;
gap: 8px;
}
.errorMessage {
background: #fee;
border: 1px solid #fcc;
background: ${cssManager.bdTheme('#fee', '#4a1f1f')};
border: 1px solid ${cssManager.bdTheme('#fcc', '#6a2f2f')};
border-radius: 4px;
padding: 16px;
color: #c00;
color: ${cssManager.bdTheme('#c00', '#ff6666')};
margin: 16px 0;
}
.loadingMessage {
text-align: center;
padding: 40px;
color: #666;
color: ${cssManager.bdTheme('#666', '#999')};
}
`,
];

View File

@ -64,21 +64,20 @@ export class OpsViewEmails extends DeesElement {
height: 100%;
}
.emailContainer {
display: grid;
grid-template-columns: 280px 1fr;
.emailLayout {
display: flex;
gap: 16px;
height: 100%;
min-height: 600px;
}
.sidebar {
display: flex;
flex-direction: column;
gap: 16px;
flex-shrink: 0;
width: 280px;
}
.mainContent {
.mainArea {
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
@ -107,22 +106,22 @@ export class OpsViewEmails extends DeesElement {
flex: 1;
display: flex;
flex-direction: column;
background: white;
border: 1px solid #e9ecef;
background: ${cssManager.bdTheme('#fff', '#222')};
border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
border-radius: 8px;
overflow: hidden;
}
.emailHeader {
padding: 24px;
border-bottom: 1px solid #e9ecef;
border-bottom: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
}
.emailSubject {
font-size: 24px;
font-weight: 600;
margin-bottom: 16px;
color: #333;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.emailMeta {
@ -130,7 +129,7 @@ export class OpsViewEmails extends DeesElement {
flex-direction: column;
gap: 8px;
font-size: 14px;
color: #666;
color: ${cssManager.bdTheme('#666', '#999')};
}
.emailMetaRow {
@ -155,8 +154,8 @@ export class OpsViewEmails extends DeesElement {
display: flex;
gap: 8px;
padding: 16px 24px;
border-top: 1px solid #e9ecef;
background: #fafafa;
border-top: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
background: ${cssManager.bdTheme('#fafafa', '#1a1a1a')};
}
.emptyState {
@ -165,7 +164,7 @@ export class OpsViewEmails extends DeesElement {
align-items: center;
justify-content: center;
height: 100%;
color: #999;
color: ${cssManager.bdTheme('#999', '#666')};
}
.emptyIcon {
@ -177,65 +176,81 @@ export class OpsViewEmails extends DeesElement {
.emptyText {
font-size: 18px;
}
.email-read {
color: ${cssManager.bdTheme('#999', '#666')};
}
.email-unread {
color: ${cssManager.bdTheme('#1976d2', '#4a90e2')};
}
.attachment-icon {
color: ${cssManager.bdTheme('#666', '#999')};
}
`,
];
public render() {
if (this.selectedEmail) {
return html`
<ops-sectionheading>Emails</ops-sectionheading>
<div class="emailLayout">
<div class="sidebar">
<dees-windowbox>
<dees-button @click=${() => this.selectedEmail = null} type="secondary" style="width: 100%;">
<dees-icon name="arrowLeft" slot="iconSlot"></dees-icon>
Back to List
</dees-button>
<dees-menu style="margin-top: 16px;">
<dees-menu-item
.active=${this.selectedFolder === 'inbox'}
@click=${() => { this.selectFolder('inbox'); this.selectedEmail = null; }}
.iconName=${'inbox'}
.label=${'Inbox'}
.badgeText=${this.getEmailCount('inbox') > 0 ? String(this.getEmailCount('inbox')) : ''}
></dees-menu-item>
<dees-menu-item
.active=${this.selectedFolder === 'sent'}
@click=${() => { this.selectFolder('sent'); this.selectedEmail = null; }}
.iconName=${'paperPlane'}
.label=${'Sent'}
.badgeText=${this.getEmailCount('sent') > 0 ? String(this.getEmailCount('sent')) : ''}
></dees-menu-item>
<dees-menu-item
.active=${this.selectedFolder === 'draft'}
@click=${() => { this.selectFolder('draft'); this.selectedEmail = null; }}
.iconName=${'file'}
.label=${'Drafts'}
.badgeText=${this.getEmailCount('draft') > 0 ? String(this.getEmailCount('draft')) : ''}
></dees-menu-item>
<dees-menu-item
.active=${this.selectedFolder === 'trash'}
@click=${() => { this.selectFolder('trash'); this.selectedEmail = null; }}
.iconName=${'trash'}
.label=${'Trash'}
.badgeText=${this.getEmailCount('trash') > 0 ? String(this.getEmailCount('trash')) : ''}
></dees-menu-item>
</dees-menu>
</dees-windowbox>
</div>
<div class="mainArea">
${this.renderEmailPreview()}
</div>
</div>
`;
}
return html`
<ops-sectionheading>Emails</ops-sectionheading>
<div class="emailContainer">
<!-- Sidebar -->
<dees-windowbox>
<dees-button @click=${() => this.openComposeModal()} type="highlighted" style="width: 100%;">
<dees-icon name="penToSquare" slot="iconSlot"></dees-icon>
Compose
</dees-button>
<dees-menu style="margin-top: 16px;">
<dees-menu-item
.active=${this.selectedFolder === 'inbox'}
@click=${() => this.selectFolder('inbox')}
.iconName=${'inbox'}
.label=${'Inbox'}
.badgeText=${this.getEmailCount('inbox') > 0 ? String(this.getEmailCount('inbox')) : ''}
></dees-menu-item>
<dees-menu-item
.active=${this.selectedFolder === 'sent'}
@click=${() => this.selectFolder('sent')}
.iconName=${'paperPlane'}
.label=${'Sent'}
.badgeText=${this.getEmailCount('sent') > 0 ? String(this.getEmailCount('sent')) : ''}
></dees-menu-item>
<dees-menu-item
.active=${this.selectedFolder === 'draft'}
@click=${() => this.selectFolder('draft')}
.iconName=${'file'}
.label=${'Drafts'}
.badgeText=${this.getEmailCount('draft') > 0 ? String(this.getEmailCount('draft')) : ''}
></dees-menu-item>
<dees-menu-item
.active=${this.selectedFolder === 'trash'}
@click=${() => this.selectFolder('trash')}
.iconName=${'trash'}
.label=${'Trash'}
.badgeText=${this.getEmailCount('trash') > 0 ? String(this.getEmailCount('trash')) : ''}
></dees-menu-item>
</dees-menu>
</dees-windowbox>
<!-- Main Content -->
<div class="mainContent">
${this.selectedEmail ? this.renderEmailPreview() : this.renderEmailListView()}
</div>
</div>
`;
}
private renderEmailListView() {
return html`
<!-- Toolbar -->
<div class="emailToolbar">
<div class="emailToolbar" style="margin-bottom: 16px;">
<dees-button @click=${() => this.openComposeModal()} type="highlighted">
<dees-icon name="penToSquare" slot="iconSlot"></dees-icon>
Compose
</dees-button>
<dees-input-text
class="searchBox"
placeholder="Search emails..."
@ -246,20 +261,50 @@ export class OpsViewEmails extends DeesElement {
</dees-input-text>
<dees-button @click=${() => this.refreshEmails()}>
${this.isLoading ? html`<dees-spinner size="small"></dees-spinner>` : html`<dees-icon name="arrowsRotate"></dees-icon>`}
${this.isLoading ? html`<dees-spinner slot="iconSlot" size="small"></dees-spinner>` : html`<dees-icon slot="iconSlot" name="arrowsRotate"></dees-icon>`}
Refresh
</dees-button>
<dees-button @click=${() => this.markAllAsRead()}>
<dees-icon name="envelopeOpen"></dees-icon>
<dees-icon name="envelopeOpen" slot="iconSlot"></dees-icon>
Mark all read
</dees-button>
<div style="margin-left: auto; display: flex; gap: 8px;">
<dees-button-group>
<dees-button
@click=${() => this.selectFolder('inbox')}
.type=${this.selectedFolder === 'inbox' ? 'highlighted' : 'normal'}
>
Inbox ${this.getEmailCount('inbox') > 0 ? `(${this.getEmailCount('inbox')})` : ''}
</dees-button>
<dees-button
@click=${() => this.selectFolder('sent')}
.type=${this.selectedFolder === 'sent' ? 'highlighted' : 'normal'}
>
Sent
</dees-button>
<dees-button
@click=${() => this.selectFolder('draft')}
.type=${this.selectedFolder === 'draft' ? 'highlighted' : 'normal'}
>
Drafts ${this.getEmailCount('draft') > 0 ? `(${this.getEmailCount('draft')})` : ''}
</dees-button>
<dees-button
@click=${() => this.selectFolder('trash')}
.type=${this.selectedFolder === 'trash' ? 'highlighted' : 'normal'}
>
Trash
</dees-button>
</dees-button-group>
</div>
</div>
<!-- Email List -->
${this.renderEmailList()}
`;
}
private renderEmailList() {
const filteredEmails = this.getFilteredEmails();
@ -276,12 +321,12 @@ export class OpsViewEmails extends DeesElement {
<dees-table
.data=${filteredEmails}
.displayFunction=${(email: IEmail) => ({
'Status': html`<dees-icon name="${email.read ? 'envelopeOpen' : 'envelope'}" style="color: ${email.read ? '#999' : '#1976d2'}"></dees-icon>`,
'Status': html`<dees-icon name="${email.read ? 'envelopeOpen' : 'envelope'}" class="${email.read ? 'email-read' : 'email-unread'}"></dees-icon>`,
From: email.from,
Subject: html`<strong style="${!email.read ? 'font-weight: 600' : ''}">${email.subject}</strong>`,
Date: this.formatDate(email.date),
'Attach': html`
${email.attachments?.length ? html`<dees-icon name="paperclip" style="color: #666"></dees-icon>` : ''}
${email.attachments?.length ? html`<dees-icon name="paperclip" class="attachment-icon"></dees-icon>` : ''}
`,
})}
.dataActions=${[
@ -365,11 +410,7 @@ export class OpsViewEmails extends DeesElement {
</div>
<div class="emailActions">
<dees-button @click=${() => this.selectedEmail = null} type="secondary">
<dees-icon name="arrowLeft" slot="iconSlot"></dees-icon>
Back to List
</dees-button>
<div style="margin-left: auto; display: flex; gap: 8px;">
<div style="display: flex; gap: 8px;">
<dees-button @click=${() => this.replyToEmail(this.selectedEmail!)}>
<dees-icon name="reply" slot="iconSlot"></dees-icon>
Reply

View File

@ -48,7 +48,7 @@ export class OpsViewLogs extends DeesElement {
}
.logContainer {
background: #1e1e1e;
background: ${cssManager.bdTheme('#f8f9fa', '#1e1e1e')};
border-radius: 8px;
padding: 16px;
max-height: 600px;
@ -63,7 +63,7 @@ export class OpsViewLogs extends DeesElement {
}
.logTimestamp {
color: #7a7a7a;
color: ${cssManager.bdTheme('#7a7a7a', '#7a7a7a')};
margin-right: 8px;
}
@ -76,33 +76,33 @@ export class OpsViewLogs extends DeesElement {
}
.logLevel.debug {
color: #6a9955;
background: rgba(106, 153, 85, 0.1);
color: ${cssManager.bdTheme('#6a9955', '#6a9955')};
background: ${cssManager.bdTheme('rgba(106, 153, 85, 0.1)', 'rgba(106, 153, 85, 0.1)')};
}
.logLevel.info {
color: #569cd6;
background: rgba(86, 156, 214, 0.1);
color: ${cssManager.bdTheme('#569cd6', '#569cd6')};
background: ${cssManager.bdTheme('rgba(86, 156, 214, 0.1)', 'rgba(86, 156, 214, 0.1)')};
}
.logLevel.warn {
color: #ce9178;
background: rgba(206, 145, 120, 0.1);
color: ${cssManager.bdTheme('#ce9178', '#ce9178')};
background: ${cssManager.bdTheme('rgba(206, 145, 120, 0.1)', 'rgba(206, 145, 120, 0.1)')};
}
.logLevel.error {
color: #f44747;
background: rgba(244, 71, 71, 0.1);
color: ${cssManager.bdTheme('#f44747', '#f44747')};
background: ${cssManager.bdTheme('rgba(244, 71, 71, 0.1)', 'rgba(244, 71, 71, 0.1)')};
}
.logCategory {
color: #c586c0;
color: ${cssManager.bdTheme('#c586c0', '#c586c0')};
margin-right: 8px;
}
.logMessage {
color: #d4d4d4;
color: ${cssManager.bdTheme('#333', '#d4d4d4')};
}
.noLogs {
color: #7a7a7a;
color: ${cssManager.bdTheme('#7a7a7a', '#7a7a7a')};
text-align: center;
padding: 40px;
}

View File

@ -74,14 +74,11 @@ export class OpsViewNetwork extends DeesElement {
}
.controlBar {
background: white;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 16px;
display: flex;
gap: 16px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 24px;
}
.controlGroup {
@ -92,7 +89,7 @@ export class OpsViewNetwork extends DeesElement {
.controlLabel {
font-size: 14px;
color: #666;
color: ${cssManager.bdTheme('#666', '#999')};
margin-right: 8px;
}
@ -114,28 +111,28 @@ export class OpsViewNetwork extends DeesElement {
}
.protocolBadge.http {
background: #e3f2fd;
color: #1976d2;
background: ${cssManager.bdTheme('#e3f2fd', '#1a2c3a')};
color: ${cssManager.bdTheme('#1976d2', '#5a9fd4')};
}
.protocolBadge.https {
background: #e8f5e9;
color: #388e3c;
background: ${cssManager.bdTheme('#e8f5e9', '#1a3a1a')};
color: ${cssManager.bdTheme('#388e3c', '#66bb6a')};
}
.protocolBadge.tcp {
background: #fff3e0;
color: #f57c00;
background: ${cssManager.bdTheme('#fff3e0', '#3a2a1a')};
color: ${cssManager.bdTheme('#f57c00', '#ff9933')};
}
.protocolBadge.smtp {
background: #f3e5f5;
color: #7b1fa2;
background: ${cssManager.bdTheme('#f3e5f5', '#2a1a3a')};
color: ${cssManager.bdTheme('#7b1fa2', '#ba68c8')};
}
.protocolBadge.dns {
background: #e0f2f1;
color: #00796b;
background: ${cssManager.bdTheme('#e0f2f1', '#1a3a3a')};
color: ${cssManager.bdTheme('#00796b', '#4db6ac')};
}
.statusBadge {
@ -148,18 +145,18 @@ export class OpsViewNetwork extends DeesElement {
}
.statusBadge.success {
background: #e8f5e9;
color: #388e3c;
background: ${cssManager.bdTheme('#e8f5e9', '#1a3a1a')};
color: ${cssManager.bdTheme('#388e3c', '#66bb6a')};
}
.statusBadge.error {
background: #ffebee;
color: #d32f2f;
background: ${cssManager.bdTheme('#ffebee', '#3a1a1a')};
color: ${cssManager.bdTheme('#d32f2f', '#ff6666')};
}
.statusBadge.warning {
background: #fff3e0;
color: #f57c00;
background: ${cssManager.bdTheme('#fff3e0', '#3a2a1a')};
color: ${cssManager.bdTheme('#f57c00', '#ff9933')};
}
`,
];

View File

@ -44,7 +44,7 @@ export class OpsViewOverview extends DeesElement {
margin: 32px 0 16px 0;
font-size: 24px;
font-weight: 600;
color: #333;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.chartGrid {
@ -57,15 +57,15 @@ export class OpsViewOverview extends DeesElement {
.loadingMessage {
text-align: center;
padding: 40px;
color: #666;
color: ${cssManager.bdTheme('#666', '#999')};
}
.errorMessage {
background-color: #fee;
border: 1px solid #fcc;
background-color: ${cssManager.bdTheme('#fee', '#4a1f1f')};
border: 1px solid ${cssManager.bdTheme('#fcc', '#6a2f2f')};
border-radius: 4px;
padding: 16px;
color: #c00;
color: ${cssManager.bdTheme('#c00', '#ff6666')};
margin: 16px 0;
}

View File

@ -10,6 +10,7 @@ import {
css,
cssManager,
} from '@design.estate/dees-element';
import { type IStatsTile } from '@design.estate/dees-catalog';
@customElement('ops-view-security')
export class OpsViewSecurity extends DeesElement {
@ -45,7 +46,7 @@ export class OpsViewSecurity extends DeesElement {
display: flex;
gap: 8px;
margin-bottom: 24px;
border-bottom: 2px solid #e9ecef;
border-bottom: 2px solid ${cssManager.bdTheme('#e9ecef', '#333')};
}
.tab {
@ -55,29 +56,33 @@ export class OpsViewSecurity extends DeesElement {
border-bottom: 2px solid transparent;
cursor: pointer;
font-size: 16px;
color: #666;
color: ${cssManager.bdTheme('#666', '#999')};
transition: all 0.2s ease;
}
.tab:hover {
color: #333;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.tab.active {
color: #2196F3;
border-bottom-color: #2196F3;
color: ${cssManager.bdTheme('#2196F3', '#4a90e2')};
border-bottom-color: ${cssManager.bdTheme('#2196F3', '#4a90e2')};
}
.securityGrid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 16px;
h2 {
margin: 32px 0 16px 0;
font-size: 24px;
font-weight: 600;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
dees-statsgrid {
margin-bottom: 32px;
}
.securityCard {
background: white;
border: 1px solid #e9ecef;
background: ${cssManager.bdTheme('#fff', '#222')};
border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
border-radius: 8px;
padding: 24px;
position: relative;
@ -85,18 +90,18 @@ export class OpsViewSecurity extends DeesElement {
}
.securityCard.alert {
border-color: #f44336;
background: #ffebee;
border-color: ${cssManager.bdTheme('#f44336', '#ff6666')};
background: ${cssManager.bdTheme('#ffebee', '#4a1f1f')};
}
.securityCard.warning {
border-color: #ff9800;
background: #fff3e0;
border-color: ${cssManager.bdTheme('#ff9800', '#ffaa33')};
background: ${cssManager.bdTheme('#fff3e0', '#4a3a1f')};
}
.securityCard.success {
border-color: #4caf50;
background: #e8f5e9;
border-color: ${cssManager.bdTheme('#4caf50', '#66cc66')};
background: ${cssManager.bdTheme('#e8f5e9', '#1f3f1f')};
}
.cardHeader {
@ -109,7 +114,7 @@ export class OpsViewSecurity extends DeesElement {
.cardTitle {
font-size: 18px;
font-weight: 600;
color: #333;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.cardStatus {
@ -120,18 +125,18 @@ export class OpsViewSecurity extends DeesElement {
}
.status-critical {
background: #f44336;
color: white;
background: ${cssManager.bdTheme('#f44336', '#ff6666')};
color: ${cssManager.bdTheme('#fff', '#fff')};
}
.status-warning {
background: #ff9800;
color: white;
background: ${cssManager.bdTheme('#ff9800', '#ffaa33')};
color: ${cssManager.bdTheme('#fff', '#fff')};
}
.status-good {
background: #4caf50;
color: white;
background: ${cssManager.bdTheme('#4caf50', '#66cc66')};
color: ${cssManager.bdTheme('#fff', '#fff')};
}
.metricValue {
@ -142,7 +147,7 @@ export class OpsViewSecurity extends DeesElement {
.metricLabel {
font-size: 14px;
color: #666;
color: ${cssManager.bdTheme('#666', '#999')};
}
.actionButton {
@ -159,7 +164,7 @@ export class OpsViewSecurity extends DeesElement {
justify-content: space-between;
align-items: center;
padding: 12px;
border-bottom: 1px solid #e9ecef;
border-bottom: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
}
.blockedIpItem:last-child {
@ -173,12 +178,12 @@ export class OpsViewSecurity extends DeesElement {
.blockReason {
font-size: 14px;
color: #666;
color: ${cssManager.bdTheme('#666', '#999')};
}
.blockTime {
font-size: 12px;
color: #999;
color: ${cssManager.bdTheme('#999', '#666')};
}
`,
];
@ -243,36 +248,60 @@ export class OpsViewSecurity extends DeesElement {
private renderOverview(metrics: any) {
const threatLevel = this.calculateThreatLevel(metrics);
const threatScore = this.getThreatScore(metrics);
const tiles: IStatsTile[] = [
{
id: 'threatLevel',
title: 'Threat Level',
value: threatScore,
type: 'gauge',
icon: 'shield',
gaugeOptions: {
min: 0,
max: 100,
thresholds: [
{ value: 0, color: '#ef4444' },
{ value: 30, color: '#f59e0b' },
{ value: 70, color: '#22c55e' },
],
},
description: `Status: ${threatLevel.toUpperCase()}`,
},
{
id: 'blockedThreats',
title: 'Blocked Threats',
value: metrics.blockedIPs.length + metrics.spamDetected,
type: 'number',
icon: 'userShield',
color: '#ef4444',
description: 'Total threats blocked today',
},
{
id: 'activeSessions',
title: 'Active Sessions',
value: 0,
type: 'number',
icon: 'users',
color: '#22c55e',
description: 'Current authenticated sessions',
},
{
id: 'authFailures',
title: 'Auth Failures',
value: metrics.authenticationFailures,
type: 'number',
icon: 'lockOpen',
color: metrics.authenticationFailures > 10 ? '#ef4444' : '#f59e0b',
description: 'Failed login attempts today',
},
];
return html`
<div class="securityGrid">
<div class="securityCard ${threatLevel}">
<div class="cardHeader">
<h3 class="cardTitle">Threat Level</h3>
<span class="cardStatus status-${threatLevel === 'alert' ? 'critical' : threatLevel === 'warning' ? 'warning' : 'good'}">
${threatLevel.toUpperCase()}
</span>
</div>
<div class="metricValue">${this.getThreatScore(metrics)}/100</div>
<div class="metricLabel">Overall security score</div>
</div>
<div class="securityCard">
<div class="cardHeader">
<h3 class="cardTitle">Blocked Threats</h3>
</div>
<div class="metricValue">${metrics.blockedIPs.length + metrics.spamDetected}</div>
<div class="metricLabel">Total threats blocked today</div>
</div>
<div class="securityCard">
<div class="cardHeader">
<h3 class="cardTitle">Active Sessions</h3>
</div>
<div class="metricValue">${0}</div>
<div class="metricLabel">Current authenticated sessions</div>
</div>
</div>
<dees-statsgrid
.tiles=${tiles}
.minTileWidth=${200}
></dees-statsgrid>
<h2>Recent Security Events</h2>
<dees-table
@ -320,20 +349,32 @@ export class OpsViewSecurity extends DeesElement {
}
private renderAuthentication(metrics: any) {
return html`
<div class="securityGrid">
<div class="securityCard">
<h3 class="cardTitle">Authentication Statistics</h3>
<div class="metricValue">${metrics.authenticationFailures}</div>
<div class="metricLabel">Failed authentication attempts today</div>
</div>
const tiles: IStatsTile[] = [
{
id: 'authFailures',
title: 'Authentication Failures',
value: metrics.authenticationFailures,
type: 'number',
icon: 'lockOpen',
color: metrics.authenticationFailures > 10 ? '#ef4444' : '#f59e0b',
description: 'Failed authentication attempts today',
},
{
id: 'successfulLogins',
title: 'Successful Logins',
value: 0,
type: 'number',
icon: 'lock',
color: '#22c55e',
description: 'Successful logins today',
},
];
<div class="securityCard">
<h3 class="cardTitle">Successful Logins</h3>
<div class="metricValue">${0}</div>
<div class="metricLabel">Successful logins today</div>
</div>
</div>
return html`
<dees-statsgrid
.tiles=${tiles}
.minTileWidth=${200}
></dees-statsgrid>
<h2>Recent Login Attempts</h2>
<dees-table
@ -352,32 +393,50 @@ export class OpsViewSecurity extends DeesElement {
}
private renderEmailSecurity(metrics: any) {
const tiles: IStatsTile[] = [
{
id: 'malware',
title: 'Malware Detection',
value: metrics.malwareDetected,
type: 'number',
icon: 'virusSlash',
color: metrics.malwareDetected > 0 ? '#ef4444' : '#22c55e',
description: 'Malware detected',
},
{
id: 'phishing',
title: 'Phishing Detection',
value: metrics.phishingDetected,
type: 'number',
icon: 'fishFins',
color: metrics.phishingDetected > 0 ? '#ef4444' : '#22c55e',
description: 'Phishing attempts detected',
},
{
id: 'suspicious',
title: 'Suspicious Activities',
value: metrics.suspiciousActivities,
type: 'number',
icon: 'triangleExclamation',
color: metrics.suspiciousActivities > 5 ? '#ef4444' : '#f59e0b',
description: 'Suspicious activities detected',
},
{
id: 'spam',
title: 'Spam Detection',
value: metrics.spamDetected,
type: 'number',
icon: 'ban',
color: '#f59e0b',
description: 'Spam emails blocked',
},
];
return html`
<div class="securityGrid">
<div class="securityCard">
<h3 class="cardTitle">Malware Detection</h3>
<div class="metricValue">${metrics.malwareDetected}</div>
<div class="metricLabel">Malware detected</div>
</div>
<div class="securityCard">
<h3 class="cardTitle">Phishing Detection</h3>
<div class="metricValue">${metrics.phishingDetected}</div>
<div class="metricLabel">Phishing attempts detected</div>
</div>
<div class="securityCard">
<h3 class="cardTitle">Suspicious Activities</h3>
<div class="metricValue">${metrics.suspiciousActivities}</div>
<div class="metricLabel">Suspicious activities detected</div>
</div>
<div class="securityCard">
<h3 class="cardTitle">Spam Detection</h3>
<div class="metricValue">${metrics.spamDetected}</div>
<div class="metricLabel">Spam emails blocked</div>
</div>
</div>
<dees-statsgrid
.tiles=${tiles}
.minTileWidth=${200}
></dees-statsgrid>
<h2>Email Security Configuration</h2>
<div class="securityCard">

View File

@ -21,14 +21,10 @@ export class OpsSectionHeading extends DeesElement {
font-family: 'Cal Sans', 'Inter', sans-serif;
font-size: 28px;
font-weight: 600;
color: #111;
color: ${cssManager.bdTheme('#111', '#fff')};
margin: 0;
padding: 0;
}
:host([theme="dark"]) .heading {
color: #fff;
}
`,
];