Files
Cleanuparr/code/frontend/src/app/features/events/events.component.html

187 lines
6.7 KiB
HTML

<app-page-header
title="Events"
subtitle="System events and notifications"
/>
<div class="page-content">
<!-- Toolbar -->
<div class="toolbar">
<div class="toolbar__filters">
<app-select
placeholder="All Severities"
[options]="severityOptions()"
[(value)]="selectedSeverity"
(valueChange)="onFilterChange()"
/>
<app-select
placeholder="All Types"
[options]="typeOptions()"
[(value)]="selectedType"
(valueChange)="onFilterChange()"
/>
<app-input
placeholder="From date"
type="datetime-local"
[(value)]="fromDate"
(blurred)="onFilterChange()"
/>
<app-input
placeholder="To date"
type="datetime-local"
[(value)]="toDate"
(blurred)="onFilterChange()"
/>
<app-input
placeholder="Search events..."
type="search"
[(value)]="searchQuery"
(blurred)="onFilterChange()"
/>
</div>
<div class="toolbar__actions">
<app-button variant="ghost" size="sm" (clicked)="refresh()">
Refresh
</app-button>
<div class="export-wrapper">
<app-button variant="ghost" size="sm" (clicked)="showExportMenu.set(!showExportMenu())">
<ng-icon name="tablerFileExport" />
Export
</app-button>
@if (showExportMenu()) {
<div class="export-menu">
<button class="export-menu__item" (click)="exportEvents('json')">JSON</button>
<button class="export-menu__item" (click)="exportEvents('csv')">CSV</button>
<button class="export-menu__item" (click)="exportEvents('text')">Text</button>
</div>
}
</div>
</div>
</div>
<!-- Active Job Run Filter -->
@if (selectedJobRunId()) {
<div class="active-filter">
<span>Showing events for job run: {{ selectedJobRunId() }}</span>
<app-button variant="ghost" size="sm" (clicked)="clearJobRunFilter()">
Clear filter
</app-button>
</div>
}
<!-- Events Count -->
<div class="event-count">
<app-animated-counter [value]="totalRecords()" [duration]="400" /> events
</div>
<!-- Events List -->
<app-card [noPadding]="true">
<div class="events-list">
@for (event of events(); track event.id) {
<div
class="event-row"
[class.event-row--expanded]="expandedId() === event.id"
>
<div
class="event-row__main"
[class.event-row__main--expandable]="isExpandable(event)"
(click)="isExpandable(event) ? toggleExpand(event.id) : null"
>
<button class="event-row__copy" (click)="copyEvent(event); $event.stopPropagation()" title="Copy event">
<ng-icon name="tablerCopy" />
</button>
<span class="event-row__time">{{ event.timestamp | date:'yyyy-MM-dd HH:mm:ss' }}</span>
<app-badge [severity]="eventSeverity(event.severity)" size="sm">
{{ event.severity }}
</app-badge>
<app-badge [severity]="eventTypeSeverity(event.eventType)" size="sm">
{{ formatEventType(event.eventType) }}
</app-badge>
@if (event.trackingId) {
<span class="event-row__tracking">{{ event.trackingId }}</span>
}
@if (isExpandable(event)) {
<ng-icon
[name]="expandedId() === event.id ? 'tablerChevronUp' : 'tablerChevronDown'"
class="event-row__chevron"
/>
}
</div>
<div class="event-row__message">{{ event.message }}</div>
@if (expandedId() === event.id) {
<div class="event-row__details">
@if (event.trackingId) {
<div class="event-row__detail">
<span class="event-row__detail-label">Tracking ID</span>
<span class="event-row__detail-value">{{ event.trackingId }}</span>
</div>
}
@if (event.instanceType || event.downloadClientType) {
<div class="event-row__detail">
<span class="event-row__detail-label">Source</span>
<div class="event-row__source">
@if (event.instanceType) {
<app-badge severity="info" size="sm">{{ event.instanceType }}</app-badge>
@if (event.instanceUrl) {
<span class="event-row__source-url">{{ event.instanceUrl }}</span>
}
}
@if (event.downloadClientType) {
<app-badge severity="accent" size="sm">{{ event.downloadClientType }}</app-badge>
@if (event.downloadClientName) {
<span class="event-row__source-url">{{ event.downloadClientName }}</span>
}
}
</div>
</div>
}
@if (event.jobRunId) {
<div class="event-row__detail">
<span class="event-row__detail-label">Job Run</span>
<div class="event-row__run-id-actions">
<button class="event-row__run-id" (click)="filterByJobRunId(event.jobRunId!); $event.stopPropagation()">
{{ event.jobRunId }}
</button>
<a class="event-row__run-id-link" routerLink="/logs" [queryParams]="{ jobRunId: event.jobRunId }" (click)="$event.stopPropagation()">
View Logs
</a>
</div>
</div>
}
@if (parseEventData(event.data); as data) {
<div class="event-row__detail">
<span class="event-row__detail-label">Event Data</span>
<div class="event-row__data">
@for (key of objectKeys(data); track key) {
<div class="event-row__data-item">
<span class="event-row__data-key">{{ key }}</span>
<span class="event-row__data-value">{{ formatValue(data[key]) }}</span>
</div>
}
</div>
</div>
}
</div>
}
</div>
} @empty {
<app-empty-state
icon="tablerBell"
heading="No events"
description="No events match your current filters."
/>
}
</div>
</app-card>
<!-- Pagination -->
@if (totalRecords() > pageSize()) {
<app-paginator
[totalRecords]="totalRecords()"
[pageSize]="pageSize()"
[currentPage]="currentPage()"
(pageChange)="onPageChange($event)"
/>
}
</div>