mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-06-12 15:56:29 -04:00
fix #34
This commit is contained in:
@@ -1,158 +1,202 @@
|
||||
<div class="logs-container">
|
||||
<!-- Connection Status Card - only shown when disconnected -->
|
||||
<p-card *ngIf="!isConnected()" styleClass="mb-3 connection-status-card">
|
||||
<div class="flex flex-column align-items-center gap-3 py-5">
|
||||
<p-progressSpinner styleClass="w-4rem h-4rem" strokeWidth="4" fill="var(--surface-ground)" animationDuration=".5s"></p-progressSpinner>
|
||||
<span class="text-xl font-medium">Connecting to server...</span>
|
||||
</div>
|
||||
</p-card>
|
||||
|
||||
<!-- Main Logs Card -->
|
||||
<p-card styleClass="logs-card">
|
||||
<!-- Card Header -->
|
||||
<ng-template pTemplate="header">
|
||||
<div class="flex align-items-center justify-content-between p-3 border-bottom-1 surface-border">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<h2 class="m-0">Application Logs</h2>
|
||||
<p-tag [severity]="isConnected() ? 'success' : 'danger'"
|
||||
[value]="isConnected() ? 'Connected' : 'Disconnected'"
|
||||
[pTooltip]="isConnected() ? 'Connected to log hub' : 'Attempting to reconnect...'"
|
||||
tooltipPosition="right"></p-tag>
|
||||
<!-- Connection Status Card - only shown when disconnected -->
|
||||
<p-card *ngIf="!isConnected()" styleClass="mb-3 connection-status-card">
|
||||
<div class="flex flex-column align-items-center gap-3 py-5">
|
||||
<p-progressSpinner
|
||||
styleClass="w-4rem h-4rem"
|
||||
strokeWidth="4"
|
||||
fill="var(--surface-ground)"
|
||||
animationDuration=".5s"
|
||||
></p-progressSpinner>
|
||||
<span class="text-xl font-medium">Connecting to server...</span>
|
||||
</div>
|
||||
<button pButton icon="pi pi-refresh" class="p-button-rounded p-button-text"
|
||||
(click)="refresh()" pTooltip="Refresh logs"
|
||||
[loading]="false"></button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-card>
|
||||
|
||||
<!-- Filters Section -->
|
||||
<div class="filter-container flex align-items-center justify-content-between flex-wrap gap-3">
|
||||
<div class="flex align-items-center gap-2 flex-wrap">
|
||||
<!-- Level Filter -->
|
||||
<p-dropdown [options]="levels()" placeholder="Filter by level"
|
||||
[showClear]="true" (onChange)="onLevelFilterChange($event.value)"
|
||||
styleClass="fixed-height-dropdown" [disabled]="!isConnected()">
|
||||
<ng-template pTemplate="selectedItem">
|
||||
<div class="level-indicator" *ngIf="levelFilter()">
|
||||
<p-tag [severity]="getSeverity(levelFilter() || '')" [value]="levelFilter() || ''" styleClass="level-tag"></p-tag>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template let-level pTemplate="item">
|
||||
<div class="level-indicator">
|
||||
<p-tag [severity]="getSeverity(level.value)" [value]="level.label" styleClass="level-tag"></p-tag>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-dropdown>
|
||||
|
||||
<!-- Category Filter -->
|
||||
<p-dropdown [options]="categories()" placeholder="Filter by category"
|
||||
[showClear]="true" (onChange)="onCategoryFilterChange($event.value)"
|
||||
styleClass="fixed-height-dropdown" [disabled]="!isConnected()">
|
||||
</p-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="flex align-items-center gap-2 flex-wrap">
|
||||
<!-- Search Filter -->
|
||||
<span class="p-input-icon-left">
|
||||
<i class="pi pi-search"></i>
|
||||
<input type="text" pInputText [(ngModel)]="searchFilter"
|
||||
(input)="onSearchChange($event)"
|
||||
placeholder="Search logs" [disabled]="!isConnected()"/>
|
||||
</span>
|
||||
|
||||
<!-- Clear Filters Button -->
|
||||
<button pButton icon="pi pi-filter-slash"
|
||||
label="Clear Filters"
|
||||
class="p-button-outlined"
|
||||
(click)="clearFilters()"
|
||||
[disabled]="!isConnected() || (!levelFilter() && !categoryFilter() && !searchFilter)"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logs Table -->
|
||||
<p-table [value]="filteredLogs()"
|
||||
styleClass="p-datatable-sm logs-table"
|
||||
[scrollable]="true"
|
||||
scrollHeight="calc(100vh - 260px)"
|
||||
[paginator]="true"
|
||||
[rows]="25"
|
||||
[showCurrentPageReport]="true"
|
||||
[rowsPerPageOptions]="[10, 25, 50, 100]"
|
||||
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} logs"
|
||||
[rowHover]="true"
|
||||
[loading]="!isConnected()">
|
||||
|
||||
<!-- Table Header -->
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th style="width: 180px">Timestamp</th>
|
||||
<th style="width: 100px">Level</th>
|
||||
<th style="width: 120px">Category</th>
|
||||
<th>Message</th>
|
||||
<th style="width: 120px" *ngIf="hasJobInfo()">Job Name</th>
|
||||
<th style="width: 120px" *ngIf="hasInstanceInfo()">Instance</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<!-- Table Body -->
|
||||
<ng-template pTemplate="body" let-log>
|
||||
<!-- Log Entry Row -->
|
||||
<tr [ngClass]="{
|
||||
'error-row': log.level === 'Error' || log.level === 'Fatal' || log.level === 'Critical',
|
||||
'warning-row': log.level === 'Warning'
|
||||
}">
|
||||
<td>
|
||||
<span class="font-medium">{{ log.timestamp | date: 'yyyy-MM-dd' }}</span>
|
||||
<div class="text-sm text-color-secondary">{{ log.timestamp | date: 'HH:mm:ss' }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<p-tag [severity]="getSeverity(log.level)" [value]="log.level"></p-tag>
|
||||
</td>
|
||||
<td>
|
||||
<span class="log-category">{{ log.category }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span [pTooltip]="log.exception ? 'Click to view stack trace' : undefined"
|
||||
tooltipPosition="top"
|
||||
[tooltipStyleClass]="log.exception ? 'visible' : 'invisible'">
|
||||
{{ log.message }}
|
||||
</span>
|
||||
</td>
|
||||
<td *ngIf="hasJobInfo()" class="font-medium">{{ log.jobName }}</td>
|
||||
<td *ngIf="hasInstanceInfo()" class="text-sm">{{ log.instanceName }}</td>
|
||||
</tr>
|
||||
|
||||
<!-- Exception Row -->
|
||||
<tr *ngIf="log.exception" class="exception-row">
|
||||
<td [attr.colspan]="hasJobInfo() && hasInstanceInfo() ? 6 : (hasJobInfo() || hasInstanceInfo() ? 5 : 4)" class="exception-cell">
|
||||
<div class="exception-content">
|
||||
<pre>{{ log.exception }}</pre>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<!-- Empty State -->
|
||||
<ng-template pTemplate="emptymessage">
|
||||
<tr>
|
||||
<td [attr.colspan]="hasJobInfo() && hasInstanceInfo() ? 6 : (hasJobInfo() || hasInstanceInfo() ? 5 : 4)">
|
||||
<div class="empty-message">
|
||||
<i class="pi pi-inbox"></i>
|
||||
<div class="empty-text" *ngIf="isConnected(); else disconnectedMessage">
|
||||
No logs found
|
||||
</div>
|
||||
<p *ngIf="isConnected()">Waiting for new logs or try adjusting your filters</p>
|
||||
<ng-template #disconnectedMessage>
|
||||
<div class="flex flex-column align-items-center gap-3">
|
||||
<div class="empty-text">Not connected to log hub</div>
|
||||
<p>Attempting to reconnect to the server...</p>
|
||||
<p-progressSpinner styleClass="w-3rem h-3rem" strokeWidth="4" fill="var(--surface-ground)" animationDuration=".5s"></p-progressSpinner>
|
||||
<!-- Main Logs Card -->
|
||||
<p-card styleClass="logs-card">
|
||||
<!-- Card Header -->
|
||||
<ng-template pTemplate="header">
|
||||
<div class="flex align-items-center justify-content-between p-3 border-bottom-1 surface-border">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<h2 class="m-0">Application Logs</h2>
|
||||
<p-tag
|
||||
[severity]="isConnected() ? 'success' : 'danger'"
|
||||
[value]="isConnected() ? 'Connected' : 'Disconnected'"
|
||||
[pTooltip]="isConnected() ? 'Connected to log hub' : 'Attempting to reconnect...'"
|
||||
tooltipPosition="right"
|
||||
></p-tag>
|
||||
</div>
|
||||
</ng-template>
|
||||
<button
|
||||
pButton
|
||||
icon="pi pi-refresh"
|
||||
class="p-button-rounded p-button-text"
|
||||
(click)="refresh()"
|
||||
pTooltip="Refresh logs"
|
||||
[loading]="false"
|
||||
></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</p-card>
|
||||
</ng-template>
|
||||
|
||||
<!-- Filters Section -->
|
||||
<div class="filter-container flex align-items-center justify-content-between flex-wrap gap-3">
|
||||
<div class="flex align-items-center gap-2 flex-wrap">
|
||||
<i class="pi pi-search"></i>
|
||||
|
||||
<!-- Search Filter -->
|
||||
<span class="p-input-icon-left">
|
||||
<input
|
||||
type="text"
|
||||
pInputText
|
||||
[(ngModel)]="searchFilter"
|
||||
(input)="onSearchChange($event)"
|
||||
placeholder="Search logs"
|
||||
[disabled]="!isConnected()"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<!-- Level Filter -->
|
||||
<p-select
|
||||
[options]="levels()"
|
||||
[(ngModel)]="levelFilter"
|
||||
placeholder="Filter by level"
|
||||
[showClear]="true"
|
||||
(onChange)="onLevelFilterChange($event.value)"
|
||||
styleClass="level-dropdown"
|
||||
[disabled]="!isConnected()"
|
||||
>
|
||||
</p-select>
|
||||
|
||||
<!-- Category Filter -->
|
||||
<p-select
|
||||
[options]="categories()"
|
||||
[(ngModel)]="categoryFilter"
|
||||
placeholder="Filter by category"
|
||||
[showClear]="true"
|
||||
(onChange)="onCategoryFilterChange($event.value)"
|
||||
styleClass="category-dropdown"
|
||||
[disabled]="!isConnected()"
|
||||
>
|
||||
</p-select>
|
||||
|
||||
<!-- Clear Filters Button -->
|
||||
<button
|
||||
pButton
|
||||
icon="pi pi-filter-slash"
|
||||
label="Clear Filters"
|
||||
class="p-button-outlined"
|
||||
(click)="clearFilters()"
|
||||
[disabled]="!isConnected() || (!levelFilter() && !categoryFilter() && !searchFilter())"
|
||||
></button>
|
||||
</div>
|
||||
|
||||
<div class="flex align-items-center gap-2 flex-wrap"></div>
|
||||
</div>
|
||||
|
||||
<!-- Logs Table -->
|
||||
<p-table
|
||||
[value]="filteredLogs()"
|
||||
styleClass="p-datatable-sm logs-table"
|
||||
[scrollable]="true"
|
||||
scrollHeight="calc(100vh - 500px)"
|
||||
[paginator]="true"
|
||||
[rows]="25"
|
||||
[showCurrentPageReport]="true"
|
||||
[rowsPerPageOptions]="[10, 25, 50, 100]"
|
||||
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} logs"
|
||||
[rowHover]="true"
|
||||
[loading]="!isConnected()"
|
||||
>
|
||||
<!-- Table Header -->
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th style="width: 180px">Timestamp</th>
|
||||
<th style="width: 100px">Level</th>
|
||||
<th style="width: 120px">Category</th>
|
||||
<th>Message</th>
|
||||
<th style="width: 120px" *ngIf="hasJobInfo()">Job Name</th>
|
||||
<th style="width: 120px" *ngIf="hasInstanceInfo()">Instance</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<!-- Table Body -->
|
||||
<ng-template pTemplate="body" let-log>
|
||||
<!-- Log Entry Row -->
|
||||
<tr
|
||||
[ngClass]="{
|
||||
'error-row': log.level === 'Error' || log.level === 'Fatal' || log.level === 'Critical',
|
||||
'warning-row': log.level === 'Warning'
|
||||
}"
|
||||
>
|
||||
<td>
|
||||
<span class="font-medium">{{ log.timestamp | date : "yyyy-MM-dd" }}</span>
|
||||
<div class="text-sm text-color-secondary">
|
||||
{{ log.timestamp | date : "HH:mm:ss" }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<p-tag [severity]="getSeverity(log.level)" [value]="log.level"></p-tag>
|
||||
</td>
|
||||
<td>
|
||||
<span class="log-category">{{ log.category }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
[pTooltip]="log.exception ? 'Click to view stack trace' : undefined"
|
||||
tooltipPosition="top"
|
||||
[tooltipStyleClass]="log.exception ? 'visible' : 'invisible'"
|
||||
>
|
||||
{{ log.message }}
|
||||
</span>
|
||||
</td>
|
||||
<td *ngIf="hasJobInfo()" class="font-medium">{{ log.jobName }}</td>
|
||||
<td *ngIf="hasInstanceInfo()" class="text-sm">
|
||||
{{ log.instanceName }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Exception Row -->
|
||||
<tr *ngIf="log.exception" class="exception-row">
|
||||
<td
|
||||
[attr.colspan]="
|
||||
hasJobInfo() && hasInstanceInfo() ? 6 : hasJobInfo() || hasInstanceInfo() ? 5 : 4
|
||||
"
|
||||
class="exception-cell"
|
||||
>
|
||||
<div class="exception-content">
|
||||
<pre>{{ log.exception }}</pre>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<!-- Empty State -->
|
||||
<ng-template pTemplate="emptymessage">
|
||||
<tr>
|
||||
<td
|
||||
[attr.colspan]="
|
||||
hasJobInfo() && hasInstanceInfo() ? 6 : hasJobInfo() || hasInstanceInfo() ? 5 : 4
|
||||
"
|
||||
>
|
||||
<div class="empty-message">
|
||||
<i class="pi pi-inbox"></i>
|
||||
<div class="empty-text" *ngIf="isConnected(); else disconnectedMessage">No logs found</div>
|
||||
<p *ngIf="isConnected()">Waiting for new logs or try adjusting your filters</p>
|
||||
<ng-template #disconnectedMessage>
|
||||
<div class="flex flex-column align-items-center gap-3">
|
||||
<div class="empty-text">Not connected to log hub</div>
|
||||
<p>Attempting to reconnect to the server...</p>
|
||||
<p-progressSpinner
|
||||
styleClass="w-3rem h-3rem"
|
||||
strokeWidth="4"
|
||||
fill="var(--surface-ground)"
|
||||
animationDuration=".5s"
|
||||
></p-progressSpinner>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</p-card>
|
||||
</div>
|
||||
|
||||
@@ -13,101 +13,7 @@
|
||||
}
|
||||
|
||||
::ng-deep {
|
||||
/* Card styling - add subtle animation on hover */
|
||||
.p-card {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: box-shadow 0.3s;
|
||||
|
||||
.p-card-content {
|
||||
flex: 1;
|
||||
padding: 0 !important;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/* Table styling - improved for readability */
|
||||
.p-datatable {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.p-datatable-wrapper {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.p-datatable-header {
|
||||
background-color: var(--surface-card);
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--surface-border);
|
||||
}
|
||||
|
||||
.p-datatable-thead > tr > th {
|
||||
background-color: var(--surface-section);
|
||||
color: var(--text-color);
|
||||
border-color: var(--surface-border);
|
||||
padding: 0.75rem 1rem;
|
||||
font-weight: 600;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--surface-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.p-datatable-tbody {
|
||||
> tr {
|
||||
transition: background-color 0.2s;
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--surface-hover);
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
background-color: var(--surface-ground);
|
||||
}
|
||||
|
||||
> td {
|
||||
padding: 0.75rem 1rem;
|
||||
border-color: var(--surface-border);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
/* Highlight rows with errors */
|
||||
&.error-row > td:first-child {
|
||||
border-left: 4px solid var(--red-500);
|
||||
}
|
||||
|
||||
&.warning-row > td:first-child {
|
||||
border-left: 4px solid var(--yellow-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-paginator {
|
||||
background-color: var(--surface-section);
|
||||
border-color: var(--surface-border);
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
/* Exception handling with improved styling */
|
||||
.exception-row {
|
||||
background-color: var(--surface-hover) !important;
|
||||
transition: max-height 0.3s;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.exception-cell {
|
||||
padding: 0 !important;
|
||||
}
|
||||
@@ -127,143 +33,6 @@
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
/* Tag styling with improved visibility */
|
||||
.p-tag {
|
||||
font-weight: 600;
|
||||
border-radius: var(--border-radius);
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&.p-tag-danger {
|
||||
background-color: var(--red-500);
|
||||
color: var(--red-50);
|
||||
}
|
||||
|
||||
&.p-tag-warning {
|
||||
background-color: var(--yellow-500);
|
||||
color: var(--yellow-900);
|
||||
}
|
||||
|
||||
&.p-tag-info {
|
||||
background-color: var(--blue-500);
|
||||
color: var(--blue-50);
|
||||
}
|
||||
|
||||
&.p-tag-success {
|
||||
background-color: var(--green-500);
|
||||
color: var(--green-50);
|
||||
}
|
||||
}
|
||||
|
||||
/* Form controls styling */
|
||||
.p-dropdown {
|
||||
min-width: 180px;
|
||||
border-radius: var(--border-radius);
|
||||
transition: box-shadow 0.2s, border-color 0.2s;
|
||||
|
||||
&:hover, &.p-focus {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
&.p-focus {
|
||||
box-shadow: 0 0 0 1px var(--primary-100);
|
||||
}
|
||||
|
||||
.p-dropdown-label {
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
/* Fix for consistent height dropdowns */
|
||||
&.fixed-height-dropdown {
|
||||
.p-dropdown-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 2.5rem; /* Fixed height for consistency */
|
||||
}
|
||||
|
||||
.level-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Ensure tags inside dropdowns maintain proper sizing */
|
||||
.level-tag {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
height: auto;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-input-icon-left {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
|
||||
input {
|
||||
border-radius: var(--border-radius);
|
||||
width: 100%;
|
||||
transition: box-shadow 0.2s, border-color 0.2s;
|
||||
padding: 0.5rem 0.75rem 0.5rem 2rem;
|
||||
|
||||
&:hover, &:focus {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1px var(--primary-100);
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
left: 0.75rem;
|
||||
color: var(--text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
/* Button styling with micro-interactions */
|
||||
.p-button {
|
||||
border-radius: var(--border-radius);
|
||||
transition: background-color 0.2s, color 0.2s, border-color 0.2s, transform 0.2s, box-shadow 0.2s;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&.p-button-outlined {
|
||||
background-color: transparent;
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-50);
|
||||
}
|
||||
}
|
||||
|
||||
&.p-button-text {
|
||||
&:hover {
|
||||
background-color: var(--surface-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&.p-button-rounded {
|
||||
&:hover {
|
||||
transform: translateY(-1px) rotate(15deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Empty state styling */
|
||||
.empty-message {
|
||||
padding: 2rem;
|
||||
@@ -288,37 +57,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media screen and (max-width: 768px) {
|
||||
.logs-container {
|
||||
height: calc(100vh - 7rem);
|
||||
|
||||
.filter-container {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
|
||||
.p-dropdown, .p-input-icon-left {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep {
|
||||
.p-datatable {
|
||||
.p-datatable-thead > tr > th {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.p-datatable-tbody > tr > td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { Subject, takeUntil } from 'rxjs';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { SelectModule } from 'primeng/select';
|
||||
import { TagModule } from 'primeng/tag';
|
||||
import { CardModule } from 'primeng/card';
|
||||
import { ToolbarModule } from 'primeng/toolbar';
|
||||
@@ -29,7 +29,7 @@ import { LogEntry } from '../../core/models/signalr.models';
|
||||
TableModule,
|
||||
InputTextModule,
|
||||
ButtonModule,
|
||||
DropdownModule,
|
||||
SelectModule,
|
||||
TagModule,
|
||||
CardModule,
|
||||
ToolbarModule,
|
||||
@@ -51,7 +51,7 @@ export class LogsViewerComponent implements OnInit, OnDestroy {
|
||||
// Filter state
|
||||
levelFilter = signal<string | null>(null);
|
||||
categoryFilter = signal<string | null>(null);
|
||||
searchFilter = '';
|
||||
searchFilter = signal<string>('');
|
||||
|
||||
// Computed values
|
||||
filteredLogs = computed(() => {
|
||||
@@ -66,7 +66,7 @@ export class LogsViewerComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
if (this.searchFilter) {
|
||||
const search = this.searchFilter.toLowerCase();
|
||||
const search = this.searchFilter().toLowerCase();
|
||||
filtered = filtered.filter(log =>
|
||||
log.message.toLowerCase().includes(search) ||
|
||||
(log.exception && log.exception.toLowerCase().includes(search)));
|
||||
@@ -112,22 +112,22 @@ export class LogsViewerComponent implements OnInit, OnDestroy {
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
onLevelFilterChange(level: string | null): void {
|
||||
onLevelFilterChange(level: string): void {
|
||||
this.levelFilter.set(level);
|
||||
}
|
||||
|
||||
onCategoryFilterChange(category: string | null): void {
|
||||
onCategoryFilterChange(category: string): void {
|
||||
this.categoryFilter.set(category);
|
||||
}
|
||||
|
||||
onSearchChange(event: Event): void {
|
||||
this.searchFilter = (event.target as HTMLInputElement).value;
|
||||
this.searchFilter.set((event.target as HTMLInputElement).value);
|
||||
}
|
||||
|
||||
clearFilters(): void {
|
||||
this.levelFilter.set(null);
|
||||
this.categoryFilter.set(null);
|
||||
this.searchFilter = '';
|
||||
this.searchFilter.set('');
|
||||
}
|
||||
|
||||
getSeverity(level: string): string {
|
||||
|
||||
Reference in New Issue
Block a user