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)", "test": "(tstest test/ --logfile --timeout 60)",
"start": "(node --max_old_space_size=250 ./cli.js)", "start": "(node --max_old_space_size=250 ./cli.js)",
"startTs": "(node cli.ts.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": { "devDependencies": {
"@git.zone/tsbuild": "^2.6.4", "@git.zone/tsbuild": "^2.6.4",
@ -29,7 +30,7 @@
"@api.global/typedserver": "^3.0.74", "@api.global/typedserver": "^3.0.74",
"@api.global/typedsocket": "^3.0.0", "@api.global/typedsocket": "^3.0.0",
"@apiclient.xyz/cloudflare": "^6.4.1", "@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", "@design.estate/dees-element": "^2.0.42",
"@push.rocks/projectinfo": "^5.0.1", "@push.rocks/projectinfo": "^5.0.1",
"@push.rocks/qenv": "^6.1.0", "@push.rocks/qenv": "^6.1.0",

10
pnpm-lock.yaml generated
View File

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

View File

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

View File

@ -64,21 +64,20 @@ export class OpsViewEmails extends DeesElement {
height: 100%; height: 100%;
} }
.emailContainer { .emailLayout {
display: grid; display: flex;
grid-template-columns: 280px 1fr;
gap: 16px; gap: 16px;
height: 100%; height: 100%;
min-height: 600px; min-height: 600px;
} }
.sidebar { .sidebar {
display: flex; flex-shrink: 0;
flex-direction: column; width: 280px;
gap: 16px;
} }
.mainContent { .mainArea {
flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16px;
@ -107,22 +106,22 @@ export class OpsViewEmails extends DeesElement {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: white; background: ${cssManager.bdTheme('#fff', '#222')};
border: 1px solid #e9ecef; border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
} }
.emailHeader { .emailHeader {
padding: 24px; padding: 24px;
border-bottom: 1px solid #e9ecef; border-bottom: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
} }
.emailSubject { .emailSubject {
font-size: 24px; font-size: 24px;
font-weight: 600; font-weight: 600;
margin-bottom: 16px; margin-bottom: 16px;
color: #333; color: ${cssManager.bdTheme('#333', '#ccc')};
} }
.emailMeta { .emailMeta {
@ -130,7 +129,7 @@ export class OpsViewEmails extends DeesElement {
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
font-size: 14px; font-size: 14px;
color: #666; color: ${cssManager.bdTheme('#666', '#999')};
} }
.emailMetaRow { .emailMetaRow {
@ -155,8 +154,8 @@ export class OpsViewEmails extends DeesElement {
display: flex; display: flex;
gap: 8px; gap: 8px;
padding: 16px 24px; padding: 16px 24px;
border-top: 1px solid #e9ecef; border-top: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
background: #fafafa; background: ${cssManager.bdTheme('#fafafa', '#1a1a1a')};
} }
.emptyState { .emptyState {
@ -165,7 +164,7 @@ export class OpsViewEmails extends DeesElement {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100%; height: 100%;
color: #999; color: ${cssManager.bdTheme('#999', '#666')};
} }
.emptyIcon { .emptyIcon {
@ -177,65 +176,81 @@ export class OpsViewEmails extends DeesElement {
.emptyText { .emptyText {
font-size: 18px; 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() { 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` return html`
<ops-sectionheading>Emails</ops-sectionheading> <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 --> <!-- 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 <dees-input-text
class="searchBox" class="searchBox"
placeholder="Search emails..." placeholder="Search emails..."
@ -246,20 +261,50 @@ export class OpsViewEmails extends DeesElement {
</dees-input-text> </dees-input-text>
<dees-button @click=${() => this.refreshEmails()}> <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>
<dees-button @click=${() => this.markAllAsRead()}> <dees-button @click=${() => this.markAllAsRead()}>
<dees-icon name="envelopeOpen"></dees-icon> <dees-icon name="envelopeOpen" slot="iconSlot"></dees-icon>
Mark all read Mark all read
</dees-button> </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> </div>
<!-- Email List -->
${this.renderEmailList()} ${this.renderEmailList()}
`; `;
} }
private renderEmailList() { private renderEmailList() {
const filteredEmails = this.getFilteredEmails(); const filteredEmails = this.getFilteredEmails();
@ -276,12 +321,12 @@ export class OpsViewEmails extends DeesElement {
<dees-table <dees-table
.data=${filteredEmails} .data=${filteredEmails}
.displayFunction=${(email: IEmail) => ({ .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, From: email.from,
Subject: html`<strong style="${!email.read ? 'font-weight: 600' : ''}">${email.subject}</strong>`, Subject: html`<strong style="${!email.read ? 'font-weight: 600' : ''}">${email.subject}</strong>`,
Date: this.formatDate(email.date), Date: this.formatDate(email.date),
'Attach': html` '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=${[ .dataActions=${[
@ -365,11 +410,7 @@ export class OpsViewEmails extends DeesElement {
</div> </div>
<div class="emailActions"> <div class="emailActions">
<dees-button @click=${() => this.selectedEmail = null} type="secondary"> <div style="display: flex; gap: 8px;">
<dees-icon name="arrowLeft" slot="iconSlot"></dees-icon>
Back to List
</dees-button>
<div style="margin-left: auto; display: flex; gap: 8px;">
<dees-button @click=${() => this.replyToEmail(this.selectedEmail!)}> <dees-button @click=${() => this.replyToEmail(this.selectedEmail!)}>
<dees-icon name="reply" slot="iconSlot"></dees-icon> <dees-icon name="reply" slot="iconSlot"></dees-icon>
Reply Reply

View File

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

View File

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

View File

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

View File

@ -10,6 +10,7 @@ import {
css, css,
cssManager, cssManager,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import { type IStatsTile } from '@design.estate/dees-catalog';
@customElement('ops-view-security') @customElement('ops-view-security')
export class OpsViewSecurity extends DeesElement { export class OpsViewSecurity extends DeesElement {
@ -45,7 +46,7 @@ export class OpsViewSecurity extends DeesElement {
display: flex; display: flex;
gap: 8px; gap: 8px;
margin-bottom: 24px; margin-bottom: 24px;
border-bottom: 2px solid #e9ecef; border-bottom: 2px solid ${cssManager.bdTheme('#e9ecef', '#333')};
} }
.tab { .tab {
@ -55,29 +56,33 @@ export class OpsViewSecurity extends DeesElement {
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 16px;
color: #666; color: ${cssManager.bdTheme('#666', '#999')};
transition: all 0.2s ease; transition: all 0.2s ease;
} }
.tab:hover { .tab:hover {
color: #333; color: ${cssManager.bdTheme('#333', '#ccc')};
} }
.tab.active { .tab.active {
color: #2196F3; color: ${cssManager.bdTheme('#2196F3', '#4a90e2')};
border-bottom-color: #2196F3; border-bottom-color: ${cssManager.bdTheme('#2196F3', '#4a90e2')};
} }
.securityGrid { h2 {
display: grid; margin: 32px 0 16px 0;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); font-size: 24px;
gap: 16px; font-weight: 600;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
dees-statsgrid {
margin-bottom: 32px; margin-bottom: 32px;
} }
.securityCard { .securityCard {
background: white; background: ${cssManager.bdTheme('#fff', '#222')};
border: 1px solid #e9ecef; border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
border-radius: 8px; border-radius: 8px;
padding: 24px; padding: 24px;
position: relative; position: relative;
@ -85,18 +90,18 @@ export class OpsViewSecurity extends DeesElement {
} }
.securityCard.alert { .securityCard.alert {
border-color: #f44336; border-color: ${cssManager.bdTheme('#f44336', '#ff6666')};
background: #ffebee; background: ${cssManager.bdTheme('#ffebee', '#4a1f1f')};
} }
.securityCard.warning { .securityCard.warning {
border-color: #ff9800; border-color: ${cssManager.bdTheme('#ff9800', '#ffaa33')};
background: #fff3e0; background: ${cssManager.bdTheme('#fff3e0', '#4a3a1f')};
} }
.securityCard.success { .securityCard.success {
border-color: #4caf50; border-color: ${cssManager.bdTheme('#4caf50', '#66cc66')};
background: #e8f5e9; background: ${cssManager.bdTheme('#e8f5e9', '#1f3f1f')};
} }
.cardHeader { .cardHeader {
@ -109,7 +114,7 @@ export class OpsViewSecurity extends DeesElement {
.cardTitle { .cardTitle {
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
color: #333; color: ${cssManager.bdTheme('#333', '#ccc')};
} }
.cardStatus { .cardStatus {
@ -120,18 +125,18 @@ export class OpsViewSecurity extends DeesElement {
} }
.status-critical { .status-critical {
background: #f44336; background: ${cssManager.bdTheme('#f44336', '#ff6666')};
color: white; color: ${cssManager.bdTheme('#fff', '#fff')};
} }
.status-warning { .status-warning {
background: #ff9800; background: ${cssManager.bdTheme('#ff9800', '#ffaa33')};
color: white; color: ${cssManager.bdTheme('#fff', '#fff')};
} }
.status-good { .status-good {
background: #4caf50; background: ${cssManager.bdTheme('#4caf50', '#66cc66')};
color: white; color: ${cssManager.bdTheme('#fff', '#fff')};
} }
.metricValue { .metricValue {
@ -142,7 +147,7 @@ export class OpsViewSecurity extends DeesElement {
.metricLabel { .metricLabel {
font-size: 14px; font-size: 14px;
color: #666; color: ${cssManager.bdTheme('#666', '#999')};
} }
.actionButton { .actionButton {
@ -159,7 +164,7 @@ export class OpsViewSecurity extends DeesElement {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 12px; padding: 12px;
border-bottom: 1px solid #e9ecef; border-bottom: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
} }
.blockedIpItem:last-child { .blockedIpItem:last-child {
@ -173,12 +178,12 @@ export class OpsViewSecurity extends DeesElement {
.blockReason { .blockReason {
font-size: 14px; font-size: 14px;
color: #666; color: ${cssManager.bdTheme('#666', '#999')};
} }
.blockTime { .blockTime {
font-size: 12px; font-size: 12px;
color: #999; color: ${cssManager.bdTheme('#999', '#666')};
} }
`, `,
]; ];
@ -243,36 +248,60 @@ export class OpsViewSecurity extends DeesElement {
private renderOverview(metrics: any) { private renderOverview(metrics: any) {
const threatLevel = this.calculateThreatLevel(metrics); 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` return html`
<div class="securityGrid"> <dees-statsgrid
<div class="securityCard ${threatLevel}"> .tiles=${tiles}
<div class="cardHeader"> .minTileWidth=${200}
<h3 class="cardTitle">Threat Level</h3> ></dees-statsgrid>
<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>
<h2>Recent Security Events</h2> <h2>Recent Security Events</h2>
<dees-table <dees-table
@ -320,20 +349,32 @@ export class OpsViewSecurity extends DeesElement {
} }
private renderAuthentication(metrics: any) { private renderAuthentication(metrics: any) {
return html` const tiles: IStatsTile[] = [
<div class="securityGrid"> {
<div class="securityCard"> id: 'authFailures',
<h3 class="cardTitle">Authentication Statistics</h3> title: 'Authentication Failures',
<div class="metricValue">${metrics.authenticationFailures}</div> value: metrics.authenticationFailures,
<div class="metricLabel">Failed authentication attempts today</div> type: 'number',
</div> 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"> return html`
<h3 class="cardTitle">Successful Logins</h3> <dees-statsgrid
<div class="metricValue">${0}</div> .tiles=${tiles}
<div class="metricLabel">Successful logins today</div> .minTileWidth=${200}
</div> ></dees-statsgrid>
</div>
<h2>Recent Login Attempts</h2> <h2>Recent Login Attempts</h2>
<dees-table <dees-table
@ -352,32 +393,50 @@ export class OpsViewSecurity extends DeesElement {
} }
private renderEmailSecurity(metrics: any) { 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` return html`
<div class="securityGrid"> <dees-statsgrid
<div class="securityCard"> .tiles=${tiles}
<h3 class="cardTitle">Malware Detection</h3> .minTileWidth=${200}
<div class="metricValue">${metrics.malwareDetected}</div> ></dees-statsgrid>
<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>
<h2>Email Security Configuration</h2> <h2>Email Security Configuration</h2>
<div class="securityCard"> <div class="securityCard">

View File

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