More webapp changes+fixes
This commit is contained in:
@@ -94,6 +94,91 @@
|
||||
</scn-metadata-value>
|
||||
</scn-metadata-grid>
|
||||
</nz-card>
|
||||
|
||||
<nz-card nzTitle="Messages" class="mt-16">
|
||||
<nz-table
|
||||
#messageTable
|
||||
[nzData]="messages()"
|
||||
[nzLoading]="loadingMessages()"
|
||||
[nzShowPagination]="false"
|
||||
[nzNoResult]="noMessagesResultTpl"
|
||||
nzSize="small"
|
||||
>
|
||||
<ng-template #noMessagesResultTpl></ng-template>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Content</th>
|
||||
<th nzWidth="0">Channel</th>
|
||||
<th nzWidth="0">Sender</th>
|
||||
<th nzWidth="0">Priority</th>
|
||||
<th nzWidth="0">Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (message of messages(); track message.message_id) {
|
||||
<tr class="clickable-row">
|
||||
<td>
|
||||
<a class="cell-link" [routerLink]="['/messages', message.message_id]">
|
||||
<div class="message-title">{{ message.title }}</div>
|
||||
<div class="message-id mono">{{ message.message_id }}</div>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="cell-link" [routerLink]="['/messages', message.message_id]">
|
||||
@if (message.content) {
|
||||
<div class="message-content">{{ message.content | slice:0:128 }}{{ message.content.length > 128 ? '...' : '' }}</div>
|
||||
} @else {
|
||||
<span class="text-muted"></span>
|
||||
}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="cell-link" [routerLink]="['/messages', message.message_id]">
|
||||
<div class="cell-name">{{ message.channel_internal_name }}</div>
|
||||
<div class="cell-id mono">{{ message.channel_id }}</div>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="cell-link" [routerLink]="['/messages', message.message_id]">
|
||||
<span style="white-space: pre">{{ message.sender_name || '-' }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="cell-link" [routerLink]="['/messages', message.message_id]">
|
||||
<nz-tag [nzColor]="getPriorityColor(message.priority)">
|
||||
{{ getPriorityLabel(message.priority) }}
|
||||
</nz-tag>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="cell-link" [routerLink]="['/messages', message.message_id]">
|
||||
<div class="timestamp-absolute">{{ message.timestamp | date:'yyyy-MM-dd HH:mm:ss' }}</div>
|
||||
<div class="timestamp-relative">{{ message.timestamp | relativeTime }}</div>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
} @empty {
|
||||
<tr>
|
||||
<td colspan="6">
|
||||
<nz-empty nzNotFoundContent="No messages sent with this key"></nz-empty>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</nz-table>
|
||||
@if (messagesTotalCount() > messagesPageSize) {
|
||||
<div class="pagination-controls">
|
||||
<nz-pagination
|
||||
[nzPageIndex]="messagesCurrentPage()"
|
||||
[nzPageSize]="messagesPageSize"
|
||||
[nzTotal]="messagesTotalCount()"
|
||||
[nzDisabled]="loadingMessages()"
|
||||
(nzPageIndexChange)="messagesGoToPage($event)"
|
||||
></nz-pagination>
|
||||
</div>
|
||||
}
|
||||
</nz-card>
|
||||
} @else {
|
||||
<nz-card>
|
||||
<div class="not-found">
|
||||
|
||||
@@ -90,9 +90,53 @@
|
||||
.timestamp-absolute {
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.timestamp-relative {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.clickable-row {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
.message-title {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.message-id {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.cell-name {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.cell-id {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.pagination-controls {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, inject, signal, OnInit } from '@angular/core';
|
||||
import { CommonModule, DatePipe } from '@angular/common';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NzCardModule } from 'ng-zorro-antd/card';
|
||||
import { NzButtonModule } from 'ng-zorro-antd/button';
|
||||
@@ -14,12 +14,15 @@ import { NzInputModule } from 'ng-zorro-antd/input';
|
||||
import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
|
||||
import { NzSelectModule } from 'ng-zorro-antd/select';
|
||||
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
|
||||
import { NzTableModule } from 'ng-zorro-antd/table';
|
||||
import { NzEmptyModule } from 'ng-zorro-antd/empty';
|
||||
import { NzPaginationModule } from 'ng-zorro-antd/pagination';
|
||||
import { ApiService } from '../../../core/services/api.service';
|
||||
import { AuthService } from '../../../core/services/auth.service';
|
||||
import { NotificationService } from '../../../core/services/notification.service';
|
||||
import { ChannelCacheService, ResolvedChannel } from '../../../core/services/channel-cache.service';
|
||||
import { UserCacheService, ResolvedUser } from '../../../core/services/user-cache.service';
|
||||
import { KeyToken, parsePermissions, TokenPermission, ChannelWithSubscription } from '../../../core/models';
|
||||
import { KeyToken, parsePermissions, TokenPermission, ChannelWithSubscription, Message } from '../../../core/models';
|
||||
import { RelativeTimePipe } from '../../../shared/pipes/relative-time.pipe';
|
||||
import { MetadataGridComponent, MetadataValueComponent } from '../../../shared/components/metadata-grid';
|
||||
|
||||
@@ -36,6 +39,7 @@ interface PermissionOption {
|
||||
CommonModule,
|
||||
DatePipe,
|
||||
FormsModule,
|
||||
RouterLink,
|
||||
NzCardModule,
|
||||
NzButtonModule,
|
||||
NzIconModule,
|
||||
@@ -48,6 +52,9 @@ interface PermissionOption {
|
||||
NzCheckboxModule,
|
||||
NzSelectModule,
|
||||
NzToolTipModule,
|
||||
NzTableModule,
|
||||
NzEmptyModule,
|
||||
NzPaginationModule,
|
||||
RelativeTimePipe,
|
||||
MetadataGridComponent,
|
||||
MetadataValueComponent,
|
||||
@@ -71,6 +78,14 @@ export class KeyDetailComponent implements OnInit {
|
||||
availableChannels = signal<ChannelWithSubscription[]>([]);
|
||||
resolvedOwner = signal<ResolvedUser | null>(null);
|
||||
|
||||
// Messages
|
||||
messages = signal<Message[]>([]);
|
||||
loadingMessages = signal(false);
|
||||
messagesPageSize = 16;
|
||||
messagesTotalCount = signal(0);
|
||||
messagesCurrentPage = signal(1);
|
||||
messagesNextPageToken = signal<string | null>(null);
|
||||
|
||||
// Edit modal
|
||||
showEditModal = signal(false);
|
||||
editKeyName = '';
|
||||
@@ -106,6 +121,7 @@ export class KeyDetailComponent implements OnInit {
|
||||
this.loading.set(false);
|
||||
this.resolveChannelNames(key);
|
||||
this.resolveOwner(key.owner_user_id);
|
||||
this.loadMessages(keyId);
|
||||
},
|
||||
error: () => {
|
||||
this.loading.set(false);
|
||||
@@ -113,6 +129,64 @@ export class KeyDetailComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
loadMessages(keyId: string, nextPageToken?: string): void {
|
||||
this.loadingMessages.set(true);
|
||||
// Load more messages than page size to ensure we get enough after filtering
|
||||
this.apiService.getMessages({
|
||||
page_size: 64,
|
||||
next_page_token: nextPageToken,
|
||||
trimmed: true
|
||||
}).subscribe({
|
||||
next: (response) => {
|
||||
// Filter messages by the key that was used to send them
|
||||
const filtered = response.messages.filter(m => m.used_key_id === keyId);
|
||||
this.messages.set(filtered.slice(0, this.messagesPageSize));
|
||||
this.messagesTotalCount.set(filtered.length);
|
||||
this.messagesNextPageToken.set(response.next_page_token || null);
|
||||
this.loadingMessages.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.loadingMessages.set(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
messagesGoToPage(page: number): void {
|
||||
const key = this.key();
|
||||
if (!key) return;
|
||||
this.messagesCurrentPage.set(page);
|
||||
if (page === 1) {
|
||||
this.loadMessages(key.keytoken_id);
|
||||
} else {
|
||||
const token = this.messagesNextPageToken();
|
||||
if (token) {
|
||||
this.loadMessages(key.keytoken_id, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewMessage(message: Message): void {
|
||||
this.router.navigate(['/messages', message.message_id]);
|
||||
}
|
||||
|
||||
getPriorityColor(priority: number): string {
|
||||
switch (priority) {
|
||||
case 0: return 'default';
|
||||
case 1: return 'blue';
|
||||
case 2: return 'orange';
|
||||
default: return 'default';
|
||||
}
|
||||
}
|
||||
|
||||
getPriorityLabel(priority: number): string {
|
||||
switch (priority) {
|
||||
case 0: return 'Low';
|
||||
case 1: return 'Normal';
|
||||
case 2: return 'High';
|
||||
default: return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
private resolveOwner(ownerId: string): void {
|
||||
this.userCacheService.resolveUser(ownerId).subscribe(resolved => {
|
||||
this.resolvedOwner.set(resolved);
|
||||
|
||||
@@ -34,50 +34,60 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (key of keys(); track key.keytoken_id) {
|
||||
<tr class="clickable-row" (click)="viewKey(key)">
|
||||
<tr class="clickable-row">
|
||||
<td>
|
||||
<div class="key-name">
|
||||
{{ key.name }}
|
||||
@if (isCurrentKey(key)) {
|
||||
<nz-tag nzColor="cyan" class="current-tag">Current</nz-tag>
|
||||
}
|
||||
</div>
|
||||
<div class="key-id mono">{{ key.keytoken_id }}</div>
|
||||
<a class="cell-link" [routerLink]="['/keys', key.keytoken_id]">
|
||||
<div class="key-name">
|
||||
{{ key.name }}
|
||||
@if (isCurrentKey(key)) {
|
||||
<nz-tag nzColor="cyan" class="current-tag">Current</nz-tag>
|
||||
}
|
||||
</div>
|
||||
<div class="key-id mono">{{ key.keytoken_id }}</div>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div class="permissions">
|
||||
@for (perm of getPermissions(key); track perm) {
|
||||
<nz-tag
|
||||
[nzColor]="getPermissionColor(perm)"
|
||||
nz-tooltip
|
||||
[nzTooltipTitle]="getPermissionLabel(perm)"
|
||||
>
|
||||
{{ perm }}
|
||||
</nz-tag>
|
||||
}
|
||||
@if (key.all_channels) {
|
||||
<nz-tag nzColor="default" nz-tooltip nzTooltipTitle="Access to all channels">
|
||||
All Channels
|
||||
</nz-tag>
|
||||
} @else if (key.channels && key.channels.length > 0) {
|
||||
@for (channelId of key.channels; track channelId) {
|
||||
<nz-tag nzColor="orange" nz-tooltip [nzTooltipTitle]="channelId">
|
||||
{{ getChannelDisplayName(channelId) }}
|
||||
<a class="cell-link" [routerLink]="['/keys', key.keytoken_id]">
|
||||
<div class="permissions">
|
||||
@for (perm of getPermissions(key); track perm) {
|
||||
<nz-tag
|
||||
[nzColor]="getPermissionColor(perm)"
|
||||
nz-tooltip
|
||||
[nzTooltipTitle]="getPermissionLabel(perm)"
|
||||
>
|
||||
{{ perm }}
|
||||
</nz-tag>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@if (key.all_channels) {
|
||||
<nz-tag nzColor="default" nz-tooltip nzTooltipTitle="Access to all channels">
|
||||
All Channels
|
||||
</nz-tag>
|
||||
} @else if (key.channels && key.channels.length > 0) {
|
||||
@for (channelId of key.channels; track channelId) {
|
||||
<nz-tag nzColor="orange" nz-tooltip [nzTooltipTitle]="channelId">
|
||||
{{ getChannelDisplayName(channelId) }}
|
||||
</nz-tag>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ key.messages_sent }}</td>
|
||||
<td>
|
||||
@if (key.timestamp_lastused) {
|
||||
<div class="timestamp-absolute">{{ key.timestamp_lastused | date:'yyyy-MM-dd HH:mm:ss' }}</div>
|
||||
<div class="timestamp-relative">{{ key.timestamp_lastused | relativeTime }}</div>
|
||||
} @else {
|
||||
<span class="text-muted">Never</span>
|
||||
}
|
||||
<a class="cell-link" [routerLink]="['/keys', key.keytoken_id]">
|
||||
{{ key.messages_sent }}
|
||||
</a>
|
||||
</td>
|
||||
<td (click)="$event.stopPropagation()">
|
||||
<td>
|
||||
<a class="cell-link" [routerLink]="['/keys', key.keytoken_id]">
|
||||
@if (key.timestamp_lastused) {
|
||||
<div class="timestamp-absolute">{{ key.timestamp_lastused | date:'yyyy-MM-dd HH:mm:ss' }}</div>
|
||||
<div class="timestamp-relative">{{ key.timestamp_lastused | relativeTime }}</div>
|
||||
} @else {
|
||||
<span class="text-muted">Never</span>
|
||||
}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
nz-button
|
||||
|
||||
@@ -95,9 +95,11 @@
|
||||
.timestamp-absolute {
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.timestamp-relative {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, inject, signal, OnInit } from '@angular/core';
|
||||
import { CommonModule, DatePipe } from '@angular/common';
|
||||
import { Router } from '@angular/router';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NzTableModule } from 'ng-zorro-antd/table';
|
||||
import { NzButtonModule } from 'ng-zorro-antd/button';
|
||||
@@ -37,6 +37,7 @@ interface PermissionOption {
|
||||
CommonModule,
|
||||
DatePipe,
|
||||
FormsModule,
|
||||
RouterLink,
|
||||
NzTableModule,
|
||||
NzButtonModule,
|
||||
NzIconModule,
|
||||
|
||||
Reference in New Issue
Block a user