Files
onebox/ui/src/app/features/dashboard/certificates-card.component.ts

100 lines
3.6 KiB
TypeScript

import { Component, Input } from '@angular/core';
import { RouterLink } from '@angular/router';
import {
CardComponent,
CardHeaderComponent,
CardTitleComponent,
CardDescriptionComponent,
CardContentComponent,
} from '../../ui/card/card.component';
interface ICertificateHealth {
valid: number;
expiringSoon: number;
expired: number;
expiringDomains: Array<{ domain: string; daysRemaining: number }>;
}
@Component({
selector: 'app-certificates-card',
standalone: true,
host: { class: 'block h-full' },
imports: [
RouterLink,
CardComponent,
CardHeaderComponent,
CardTitleComponent,
CardDescriptionComponent,
CardContentComponent,
],
template: `
<ui-card class="h-full">
<ui-card-header class="flex flex-col space-y-1.5">
<ui-card-title>Certificates</ui-card-title>
<ui-card-description>SSL/TLS certificate status</ui-card-description>
</ui-card-header>
<ui-card-content class="space-y-3">
<!-- Status summary -->
<div class="space-y-2">
@if (health.valid > 0) {
<div class="flex items-center gap-2">
<svg class="h-4 w-4 text-success" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
</svg>
<span class="text-sm">{{ health.valid }} valid</span>
</div>
}
@if (health.expiringSoon > 0) {
<div class="flex items-center gap-2">
<svg class="h-4 w-4 text-warning" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<span class="text-sm text-warning">{{ health.expiringSoon }} expiring soon</span>
</div>
}
@if (health.expired > 0) {
<div class="flex items-center gap-2">
<svg class="h-4 w-4 text-destructive" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
<span class="text-sm text-destructive">{{ health.expired }} expired</span>
</div>
}
@if (health.valid === 0 && health.expiringSoon === 0 && health.expired === 0) {
<div class="text-sm text-muted-foreground">No certificates</div>
}
</div>
<!-- Expiring domains list -->
@if (health.expiringDomains.length > 0) {
<div class="border-t pt-2 space-y-1">
@for (item of health.expiringDomains; track item.domain) {
<a [routerLink]="['/network']"
class="flex items-center justify-between text-sm py-1 hover:bg-muted/50 rounded px-1 -mx-1 transition-colors">
<span class="truncate text-muted-foreground">{{ item.domain }}</span>
<span
class="ml-2 whitespace-nowrap"
[class.text-warning]="item.daysRemaining > 7"
[class.text-destructive]="item.daysRemaining <= 7">
{{ item.daysRemaining }}d
</span>
</a>
}
</div>
}
</ui-card-content>
</ui-card>
`,
})
export class CertificatesCardComponent {
@Input() health: ICertificateHealth = {
valid: 0,
expiringSoon: 0,
expired: 0,
expiringDomains: [],
};
}