Simple Managment webapp [LLM]
This commit is contained in:
@@ -36,11 +36,21 @@
|
||||
{{ getChannelDisplayName(channel) }}
|
||||
</nz-tag>
|
||||
}
|
||||
@for (sender of senderFilter; track sender) {
|
||||
<nz-tag nzMode="closeable" (nzOnClose)="removeSenderFilter(sender)">
|
||||
{{ sender }}
|
||||
</nz-tag>
|
||||
}
|
||||
@if (priorityFilter.length > 0) {
|
||||
<nz-tag nzMode="closeable" (nzOnClose)="clearPriorityFilter()">
|
||||
{{ getPriorityLabel(+priorityFilter[0]) }}
|
||||
</nz-tag>
|
||||
}
|
||||
@if (dateRange) {
|
||||
<nz-tag nzMode="closeable" (nzOnClose)="clearDateRange()">
|
||||
{{ getDateRangeDisplay() }}
|
||||
</nz-tag>
|
||||
}
|
||||
<a class="clear-all" (click)="clearAllFilters()">Clear all</a>
|
||||
</div>
|
||||
}
|
||||
@@ -64,14 +74,35 @@
|
||||
[nzFilterMultiple]="true"
|
||||
(nzFilterChange)="onChannelFilterChange($event)"
|
||||
>Channel</th>
|
||||
<th nzWidth="15%">Sender</th>
|
||||
<th
|
||||
nzWidth="15%"
|
||||
[nzFilters]="senderFilters()"
|
||||
[nzFilterMultiple]="true"
|
||||
(nzFilterChange)="onSenderFilterChange($event)"
|
||||
>Sender</th>
|
||||
<th
|
||||
nzWidth="10%"
|
||||
[nzFilters]="priorityFilters"
|
||||
[nzFilterMultiple]="false"
|
||||
(nzFilterChange)="onPriorityFilterChange($event)"
|
||||
>Priority</th>
|
||||
<th nzWidth="20%">Time</th>
|
||||
<th nzWidth="20%" nzCustomFilter>
|
||||
Time
|
||||
<nz-filter-trigger [(nzVisible)]="dateFilterVisible" [nzActive]="!!dateRange" [nzDropdownMenu]="dateMenu">
|
||||
<span nz-icon nzType="filter" nzTheme="fill"></span>
|
||||
</nz-filter-trigger>
|
||||
<nz-dropdown-menu #dateMenu="nzDropdownMenu">
|
||||
<div class="date-filter-dropdown" (click)="$event.stopPropagation()">
|
||||
<nz-range-picker
|
||||
[ngModel]="dateRange"
|
||||
(ngModelChange)="onDateRangeChange($event)"
|
||||
[nzAllowClear]="true"
|
||||
nzFormat="yyyy-MM-dd"
|
||||
[nzInline]="true"
|
||||
></nz-range-picker>
|
||||
</div>
|
||||
</nz-dropdown-menu>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.date-filter-dropdown {
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.active-filters {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -11,6 +11,8 @@ import { NzEmptyModule } from 'ng-zorro-antd/empty';
|
||||
import { NzSpinModule } from 'ng-zorro-antd/spin';
|
||||
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
|
||||
import { NzPaginationModule } from 'ng-zorro-antd/pagination';
|
||||
import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
|
||||
import { NzDropDownModule } from 'ng-zorro-antd/dropdown';
|
||||
import { ApiService } from '../../../core/services/api.service';
|
||||
import { AuthService } from '../../../core/services/auth.service';
|
||||
import { Message, MessageListParams } from '../../../core/models';
|
||||
@@ -31,6 +33,8 @@ import { RelativeTimePipe } from '../../../shared/pipes/relative-time.pipe';
|
||||
NzSpinModule,
|
||||
NzToolTipModule,
|
||||
NzPaginationModule,
|
||||
NzDatePickerModule,
|
||||
NzDropDownModule,
|
||||
RelativeTimePipe,
|
||||
],
|
||||
templateUrl: './message-list.component.html',
|
||||
@@ -54,6 +58,9 @@ export class MessageListComponent implements OnInit {
|
||||
appliedSearchText = '';
|
||||
priorityFilter: string[] = [];
|
||||
channelFilter: string[] = [];
|
||||
senderFilter: string[] = [];
|
||||
dateRange: [Date, Date] | null = null;
|
||||
dateFilterVisible = false;
|
||||
|
||||
// Filter options
|
||||
priorityFilters: NzTableFilterList = [
|
||||
@@ -62,9 +69,11 @@ export class MessageListComponent implements OnInit {
|
||||
{ text: 'High', value: '2' },
|
||||
];
|
||||
channelFilters = signal<NzTableFilterList>([]);
|
||||
senderFilters = signal<NzTableFilterList>([]);
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadChannels();
|
||||
this.loadSenders();
|
||||
this.loadMessages();
|
||||
}
|
||||
|
||||
@@ -77,7 +86,20 @@ export class MessageListComponent implements OnInit {
|
||||
this.channelFilters.set(
|
||||
response.channels.map(ch => ({
|
||||
text: ch.display_name,
|
||||
value: ch.internal_name,
|
||||
value: ch.channel_id,
|
||||
}))
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadSenders(): void {
|
||||
this.apiService.getSenderNames().subscribe({
|
||||
next: (response) => {
|
||||
this.senderFilters.set(
|
||||
response.sender_names.map(s => ({
|
||||
text: s.name,
|
||||
value: s.name,
|
||||
}))
|
||||
);
|
||||
}
|
||||
@@ -95,11 +117,18 @@ export class MessageListComponent implements OnInit {
|
||||
if (this.appliedSearchText) {
|
||||
params.search = this.appliedSearchText;
|
||||
}
|
||||
if (this.priorityFilter.length === 1) {
|
||||
params.priority = parseInt(this.priorityFilter[0], 10);
|
||||
if (this.priorityFilter.length > 0) {
|
||||
params.priority = this.priorityFilter.map(p => parseInt(p, 10));
|
||||
}
|
||||
if (this.channelFilter.length > 0) {
|
||||
params.channel = this.channelFilter.join(',');
|
||||
params.channel_id = this.channelFilter;
|
||||
}
|
||||
if (this.senderFilter.length > 0) {
|
||||
params.sender = this.senderFilter;
|
||||
}
|
||||
if (this.dateRange) {
|
||||
params.after = this.dateRange[0].toISOString();
|
||||
params.before = this.dateRange[1].toISOString();
|
||||
}
|
||||
|
||||
// Use page-index based pagination: $1 = page 1, $2 = page 2, etc.
|
||||
@@ -138,6 +167,12 @@ export class MessageListComponent implements OnInit {
|
||||
this.loadMessages();
|
||||
}
|
||||
|
||||
onSenderFilterChange(filters: string[] | null): void {
|
||||
this.senderFilter = filters ?? [];
|
||||
this.currentPage.set(1);
|
||||
this.loadMessages();
|
||||
}
|
||||
|
||||
clearSearch(): void {
|
||||
this.searchText = '';
|
||||
this.appliedSearchText = '';
|
||||
@@ -157,29 +192,64 @@ export class MessageListComponent implements OnInit {
|
||||
this.loadMessages();
|
||||
}
|
||||
|
||||
clearSenderFilter(): void {
|
||||
this.senderFilter = [];
|
||||
this.currentPage.set(1);
|
||||
this.loadMessages();
|
||||
}
|
||||
|
||||
removeSenderFilter(sender: string): void {
|
||||
this.senderFilter = this.senderFilter.filter(s => s !== sender);
|
||||
this.currentPage.set(1);
|
||||
this.loadMessages();
|
||||
}
|
||||
|
||||
clearPriorityFilter(): void {
|
||||
this.priorityFilter = [];
|
||||
this.currentPage.set(1);
|
||||
this.loadMessages();
|
||||
}
|
||||
|
||||
onDateRangeChange(dates: [Date, Date] | null): void {
|
||||
this.dateRange = dates;
|
||||
if (dates) {
|
||||
this.dateFilterVisible = false;
|
||||
}
|
||||
this.currentPage.set(1);
|
||||
this.loadMessages();
|
||||
}
|
||||
|
||||
clearDateRange(): void {
|
||||
this.dateRange = null;
|
||||
this.currentPage.set(1);
|
||||
this.loadMessages();
|
||||
}
|
||||
|
||||
clearAllFilters(): void {
|
||||
this.searchText = '';
|
||||
this.appliedSearchText = '';
|
||||
this.channelFilter = [];
|
||||
this.senderFilter = [];
|
||||
this.priorityFilter = [];
|
||||
this.dateRange = null;
|
||||
this.currentPage.set(1);
|
||||
this.loadMessages();
|
||||
}
|
||||
|
||||
hasActiveFilters(): boolean {
|
||||
return !!this.appliedSearchText || this.channelFilter.length > 0 || this.priorityFilter.length > 0;
|
||||
return !!this.appliedSearchText || this.channelFilter.length > 0 || this.senderFilter.length > 0 || this.priorityFilter.length > 0 || !!this.dateRange;
|
||||
}
|
||||
|
||||
getChannelDisplayName(internalName: string): string {
|
||||
getChannelDisplayName(channelId: string): string {
|
||||
const filters = this.channelFilters();
|
||||
const channel = filters.find(f => f.value === internalName);
|
||||
return channel?.text?.toString() ?? internalName;
|
||||
const channel = filters.find(f => f.value === channelId);
|
||||
return channel?.text?.toString() ?? channelId;
|
||||
}
|
||||
|
||||
getDateRangeDisplay(): string {
|
||||
if (!this.dateRange) return '';
|
||||
const format = (d: Date) => d.toLocaleDateString();
|
||||
return `${format(this.dateRange[0])} - ${format(this.dateRange[1])}`;
|
||||
}
|
||||
|
||||
goToPage(page: number): void {
|
||||
|
||||
Reference in New Issue
Block a user