mirror of
https://github.com/MikroWizard/mikrofront.git
synced 2026-05-17 09:11:30 +00:00
Upgrade to angular 18, Replace gui-grid with PrimeNG table since gui-grid not developed anymore,Fix typo , Fix minor ui/ux bugs
This commit is contained in:
parent
e95304af3e
commit
9298dd80b6
74 changed files with 4845 additions and 3913 deletions
|
|
@ -116,217 +116,217 @@
|
|||
</c-row>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<gui-grid #grid [rowClass]="rowClass" [source]="source" [searching]="searching" [paging]="paging"
|
||||
[columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel" [autoResizeWidth]="true"
|
||||
<div class="mb-3 d-flex justify-content-end">
|
||||
<span class="p-input-icon-left">
|
||||
<i class="pi pi-search"></i>
|
||||
<input type="text" pInputText placeholder="Search peers..." (input)="applyFilterGlobal($event, 'contains')"
|
||||
class="form-control-sm" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p-table #dt [value]="source" [paginator]="true" [rows]="10" [showCurrentPageReport]="true"
|
||||
[rowsPerPageOptions]="[10, 25, 50]" [resizableColumns]="true" columnResizeMode="expand"
|
||||
[showGridlines]="true" [stripedRows]="true"
|
||||
styleClass="p-datatable-sm p-datatable-gridlines p-datatable-striped"
|
||||
[globalFilterFields]="['name', 'assigned_ip', 'public_key', 'description']"
|
||||
[loading]="loading">
|
||||
|
||||
<gui-grid-column header="Status" field="status" [width]="50" align="center">
|
||||
<ng-template let-item="item">
|
||||
<i *ngIf="item.status === 'online'" class="fa-solid fa-circle text-success" cTooltip="Online"></i>
|
||||
<i *ngIf="item.status === 'offline'" class="fa-solid fa-circle text-danger" cTooltip="Offline"></i>
|
||||
<i *ngIf="item.status === 'unreachable'" class="fa-solid fa-triangle-exclamation text-warning"
|
||||
cTooltip="Unreachable (Handshake OK, Ping Failed)"></i>
|
||||
<i *ngIf="!item.status && item.is_enabled" class="fa-solid fa-circle text-info" cTooltip="Enabled"></i>
|
||||
<i *ngIf="!item.status && !item.is_enabled" class="fa-solid fa-circle text-secondary"
|
||||
cTooltip="Disabled"></i>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
<gui-grid-column header="Identity & IP" field="name" [width]="220">
|
||||
<ng-template let-item="item">
|
||||
<div style="line-height: 1.2;">
|
||||
<i *ngIf="item.mt_user" class="fas fa-server text-info me-1" cTooltip="Managed MikroTik Device"
|
||||
style="vertical-align: middle; font-size: 0.9rem;"></i>
|
||||
<strong class="text-primary fs-6 text-truncate d-inline-block"
|
||||
style="max-width: 140px; vertical-align: middle;" [title]="item.name || item.assigned_ip">
|
||||
{{ item.name || item.assigned_ip || (item.public_key | slice:0:8) + '...' }}
|
||||
</strong>
|
||||
<c-badge *ngIf="item.is_managed === false" color="secondary" class="ms-1"
|
||||
style="vertical-align: middle; font-size: 0.65rem;">Unmanaged</c-badge>
|
||||
</div>
|
||||
<div style="font-size: 0.85rem; color: #495057;" class="fw-semibold ms-2">
|
||||
<i class="fas fa-network-wired opacity-75"></i> {{ item.assigned_ip || 'No IP' }}
|
||||
</div>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
<gui-grid-column header="Details & Description" field="description">
|
||||
<ng-template let-item="item">
|
||||
<div class="d-flex flex-column justify-content-center w-100">
|
||||
<!-- Description (Bigger if exists, hidden if empty) -->
|
||||
<div *ngIf="item.description" class="text-muted mb-1 text-wrap"
|
||||
style="font-size: 0.88rem; line-height: 1.3; font-weight: 500;" [title]="item.description">
|
||||
{{ item.description }}
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th style="width: 50px" class="text-center" pResizableColumn>Status</th>
|
||||
<th pSortableColumn="name" style="width: 200px" pResizableColumn>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>Identity & IP</span>
|
||||
<span>
|
||||
<p-sortIcon field="name"></p-sortIcon>
|
||||
<p-columnFilter type="text" field="name" display="menu" class="ms-auto" />
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th pSortableColumn="description" pResizableColumn>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>Details & Description</span>
|
||||
<span>
|
||||
<p-sortIcon field="description"></p-sortIcon>
|
||||
<p-columnFilter type="text" field="description" display="menu" class="ms-auto" />
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th pSortableColumn="nat_mode" style="width: 140px" pResizableColumn>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>Routing</span>
|
||||
<span>
|
||||
<p-sortIcon field="nat_mode"></p-sortIcon>
|
||||
<p-columnFilter type="text" field="nat_mode" display="menu" class="ms-auto" />
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th style="width: 220px" class="text-center" pResizableColumn>Integration</th>
|
||||
<th pSortableColumn="stats.last_handshake" style="width: 130px" class="text-center" pResizableColumn>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>Last Handshake</span>
|
||||
<p-sortIcon field="stats.last_handshake"></p-sortIcon>
|
||||
</div>
|
||||
</th>
|
||||
<th style="width: 130px" pResizableColumn>Transfer Speed</th>
|
||||
<th style="width: 120px" pResizableColumn>Total Volume</th>
|
||||
<th pSortableColumn="is_enabled" style="width: 90px" class="text-center" pResizableColumn>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>State</span>
|
||||
<p-sortIcon field="is_enabled"></p-sortIcon>
|
||||
</div>
|
||||
</th>
|
||||
<th style="width: 70px" class="text-center" pResizableColumn>Actions</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<!-- Peer Info (Horizontal flex, smaller if Description exists) -->
|
||||
<div style="color: #adb5bd;" class="d-flex flex-row align-items-center gap-3"
|
||||
[ngStyle]="{'font-size': item.description ? '0.7rem' : '0.8rem', 'margin-top': item.description ? '4px' : '0'}">
|
||||
<div class="text-truncate" title="Public Key" style="max-width: 140px;">
|
||||
<i class="fas fa-key me-1 opacity-75"></i> {{ item.public_key | slice:0:16 }}...
|
||||
<ng-template pTemplate="body" let-item>
|
||||
<tr [ngClass]="{'row-highlighted': item.status === 'online'}">
|
||||
<td class="text-center">
|
||||
<i *ngIf="item.status === 'online'" class="fa-solid fa-circle text-success" [pTooltip]="'Online'" tooltipPosition="top"></i>
|
||||
<i *ngIf="item.status === 'offline'" class="fa-solid fa-circle text-danger" [pTooltip]="'Offline'" tooltipPosition="top"></i>
|
||||
<i *ngIf="item.status === 'unreachable'" class="fa-solid fa-triangle-exclamation text-warning"
|
||||
[pTooltip]="'Unreachable (Handshake OK, Ping Failed)'" tooltipPosition="top"></i>
|
||||
<i *ngIf="!item.status && item.is_enabled" class="fa-solid fa-circle text-info" [pTooltip]="'Enabled'" tooltipPosition="top"></i>
|
||||
<i *ngIf="!item.status && !item.is_enabled" class="fa-solid fa-circle text-secondary"
|
||||
[pTooltip]="'Disabled'" tooltipPosition="top"></i>
|
||||
</td>
|
||||
<td>
|
||||
<div style="line-height: 1.2;">
|
||||
<i *ngIf="item.mt_user" class="fas fa-server text-info me-1" [pTooltip]="'Managed MikroTik Device'"
|
||||
style="vertical-align: middle; font-size: 0.9rem;"></i>
|
||||
<strong class="text-primary fs-6 text-truncate d-inline-block"
|
||||
style="max-width: 140px; vertical-align: middle;" [title]="item.name || item.assigned_ip">
|
||||
{{ item.name || item.assigned_ip || (item.public_key | slice:0:8) + '...' }}
|
||||
</strong>
|
||||
<c-badge *ngIf="item.is_managed === false" color="secondary" class="ms-1"
|
||||
style="vertical-align: middle; font-size: 0.65rem;">Unmanaged</c-badge>
|
||||
</div>
|
||||
<div style="font-size: 0.85rem; color: #495057;" class="fw-semibold ms-2">
|
||||
<i class="fas fa-network-wired opacity-75"></i> {{ item.assigned_ip || 'No IP' }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column justify-content-center w-100">
|
||||
<div *ngIf="item.description" class="text-muted mb-1 text-wrap small fw-bold" [title]="item.description">
|
||||
{{ item.description }}
|
||||
</div>
|
||||
<div class="text-truncate" title="Created At">
|
||||
<i class="fas fa-clock me-1 opacity-75"></i> {{ item.created_at | date:'mediumDate' }}
|
||||
<div style="color: #adb5bd; font-size: 0.75rem" class="d-flex flex-row align-items-center gap-3">
|
||||
<div class="text-truncate" title="Public Key" style="max-width: 140px;">
|
||||
<i class="fas fa-key me-1 opacity-75"></i> {{ item.public_key | slice:0:16 }}...
|
||||
</div>
|
||||
<div class="text-truncate" title="Created At">
|
||||
<i class="fas fa-clock me-1 opacity-75"></i> {{ item.created_at | date:'mediumDate' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
<gui-grid-column header="Routing & Identity" field="nat_mode" [width]="170">
|
||||
<ng-template let-value="item.nat_mode" let-item="item">
|
||||
<div class="mb-1 mx-1">
|
||||
<c-badge size="sm"
|
||||
[color]="value === 'full' ? 'success' : (value === 'split' ? 'info' : 'secondary')">
|
||||
{{ value | uppercase }}
|
||||
</c-badge>
|
||||
</div>
|
||||
<div style="font-size: 0.75rem;margin-top: 5px; " class="text-muted">
|
||||
<i class="fas fa-network-wired"></i> Iface: {{ item.custom_interface || 'Auto' }}
|
||||
</div>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
<gui-grid-column header="Integration" align="center" field="linked_device_id" [width]="250">
|
||||
<ng-template let-item="item">
|
||||
<div class="mb-1">
|
||||
<a *ngIf="item.linked_device_id" [routerLink]="['/device-stats', {id: item.linked_device_id}]"
|
||||
target="_blank" class="btn btn-sm btn-outline-primary py-0 px-2"
|
||||
style="font-size: 0.75rem; transition: all 0.2s;">
|
||||
<i class="fa-solid fa-link"></i> View Device
|
||||
</a>
|
||||
<div class="d-flex flex-row justify-content-center w-100 ng-star-inserted">
|
||||
<small *ngIf="!item.linked_device_id && !item.scan_status && item.mt_user" class="text-muted">Device
|
||||
Not Linked</small>
|
||||
|
||||
<!-- Scan Status Micro-animations -->
|
||||
<div style="font-size: 0.75rem;"
|
||||
*ngIf="item.scan_status &&item.scan_status === 'starting' || item.scan_status === 'running'"
|
||||
class="text-info mx-1 mt-1">
|
||||
<i class="fas fa-circle-notch fa-spin me-1"></i> Scanning...
|
||||
</td>
|
||||
<td>
|
||||
<div class="mb-1">
|
||||
<c-badge size="sm"
|
||||
[color]="item.nat_mode === 'full' ? 'success' : (item.nat_mode === 'split' ? 'info' : 'secondary')">
|
||||
{{ item.nat_mode | uppercase }}
|
||||
</c-badge>
|
||||
</div>
|
||||
<div style="font-size: 0.75rem;" class="text-muted mt-1">
|
||||
<i class="fas fa-network-wired small opacity-50"></i> Iface: {{ item.custom_interface || 'Auto' }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="d-flex flex-column align-items-center gap-1">
|
||||
<a *ngIf="item.linked_device_id" [routerLink]="['/device-stats', {id: item.linked_device_id}]"
|
||||
target="_blank" class="btn btn-sm btn-outline-primary py-0 px-2" style="font-size: 0.7rem;">
|
||||
<i class="fa-solid fa-link"></i> View Device
|
||||
</a>
|
||||
<div class="d-flex flex-row justify-content-center w-100 container-fluid p-0">
|
||||
<small *ngIf="!item.linked_device_id && !item.scan_status && item.mt_user" class="text-muted" style="font-size: 0.65rem;">Not Linked</small>
|
||||
<div style="font-size: 0.7rem;" *ngIf="item.scan_status === 'starting' || item.scan_status === 'running'" class="text-info">
|
||||
<i class="fas fa-circle-notch fa-spin me-1"></i> Scanning...
|
||||
</div>
|
||||
<div style="font-size: 0.7rem;" *ngIf="item.scan_status === 'completed'" class="text-success">
|
||||
<i class="fas fa-check-circle me-1"></i> Success
|
||||
</div>
|
||||
<div style="font-size: 0.7rem;" *ngIf="item.scan_status === 'failed'" class="text-danger" [pTooltip]="'MikroTik connection failed'" tooltipPosition="top">
|
||||
<i class="fas fa-exclamation-triangle me-1"></i> Failed
|
||||
</div>
|
||||
</div>
|
||||
<div style="font-size: 0.75rem;" *ngIf="item.scan_status && item.scan_status === 'completed'"
|
||||
class="text-success mx-1 mt-1" style="animation: fadeIn 0.5s ease-in-out;">
|
||||
<i class="fas fa-check-circle me-1"></i> Scan Completed
|
||||
</div>
|
||||
|
||||
<div style="font-size: 0.75rem;" *ngIf="item.scan_status &&item.scan_status === 'failed'"
|
||||
class="text-danger mx-1 mt-1" cTooltip="MikroTik connection or credential validation failed">
|
||||
<i class="fas fa-exclamation-triangle me-1"></i> Scan Failed
|
||||
</div>
|
||||
|
||||
<div style="font-size: 0.75rem;" class="text-secondary mx-1 mt-1">
|
||||
<div style="font-size: 0.65rem;" class="text-secondary opacity-75">
|
||||
<i class="fas fa-heartbeat"></i> Keepalive: {{ item.persistent_keepalive }}s
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
<gui-grid-column align="center" header="Last Handshake" field="stats" [width]="150">
|
||||
<ng-template let-item="item">
|
||||
<div *ngIf="item.stats" style="font-size: 0.85rem;" class="mt-1">
|
||||
<span *ngIf="item.stats.last_handshake > 0" class="text-secondary fw-semibold">
|
||||
</td>
|
||||
<td class="text-center small">
|
||||
<div *ngIf="item.stats?.last_handshake > 0" class="text-secondary fw-semibold">
|
||||
<i class="fas fa-handshake"></i> {{ item.stats.last_handshake * 1000 | date:'shortTime' }}<br>
|
||||
<small class="text-muted">{{ item.stats.last_handshake * 1000 | date:'shortDate' }}</small>
|
||||
</span>
|
||||
<span *ngIf="!item.stats.last_handshake || item.stats.last_handshake === 0" class="text-muted">
|
||||
</div>
|
||||
<div *ngIf="!item.stats?.last_handshake || item.stats.last_handshake === 0" class="text-muted">
|
||||
<i class="fas fa-handshake"></i> None
|
||||
</span>
|
||||
</div>
|
||||
<span *ngIf="!item.stats" class="text-muted text-sm">-</span>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
<gui-grid-column align="center" header="Transfer Speed" field="stats" [width]="130">
|
||||
<ng-template let-item="item">
|
||||
<div *ngIf="item.stats" class="mt-1 flex-column d-flex align-items-start"
|
||||
style="font-size: 0.75rem; font-family: monospace; font-weight: bold;">
|
||||
<span [ngStyle]="{'color': item.stats.rx_speed !== 0 ? '#2eb85c' : '#8a93a2'}"
|
||||
style="transition: color 0.3s ease;">
|
||||
<i class="fas fa-arrow-down opacity-75"></i> {{ formatBytes(item.stats.rx_speed || 0) }}/s
|
||||
</span>
|
||||
<span [ngStyle]="{'color': item.stats.tx_speed !== 0 ? '#3399ff' : '#8a93a2'}" class="mt-1"
|
||||
style="transition: color 0.3s ease;">
|
||||
<i class="fas fa-arrow-up opacity-75"></i> {{ formatBytes(item.stats.tx_speed || 0) }}/s
|
||||
</span>
|
||||
</div>
|
||||
<span *ngIf="!item.stats" class="text-muted text-sm">-</span>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
<gui-grid-column align="center" header="Total Volume" field="stats" [width]="120">
|
||||
<ng-template let-item="item">
|
||||
<div *ngIf="item.stats" class="mt-1 flex-column d-flex align-items-start" style="font-size: 0.75rem;">
|
||||
<span class="text-success"><i class="fas fa-arrow-down opacity-50"></i> {{(item.stats.rx_bytes /
|
||||
1048576) | number:'1.2-2'}} MB</span>
|
||||
<span class="text-info mt-1"><i class="fas fa-arrow-up opacity-50"></i> {{(item.stats.tx_bytes /
|
||||
1048576) | number:'1.2-2'}} MB</span>
|
||||
</div>
|
||||
<span *ngIf="!item.stats" class="text-muted text-sm">-</span>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
<gui-grid-column align="center" header="State" field="is_enabled" [width]="120">
|
||||
<ng-template let-item="item">
|
||||
<div class="mt-2 d-flex justify-content-center">
|
||||
<c-form-check [switch]="true">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div *ngIf="item.stats" class="d-flex flex-column" style="font-size: 0.72rem; font-family: monospace; font-weight: bold;">
|
||||
<span [style.color]="item.stats.rx_speed !== 0 ? '#2eb85c' : '#8a93a2'">
|
||||
<i class="fas fa-arrow-down opacity-75"></i> {{ formatBytes(item.stats.rx_speed || 0) }}/s
|
||||
</span>
|
||||
<span [style.color]="item.stats.tx_speed !== 0 ? '#3399ff' : '#8a93a2'">
|
||||
<i class="fas fa-arrow-up opacity-75"></i> {{ formatBytes(item.stats.tx_speed || 0) }}/s
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div *ngIf="item.stats" class="d-flex flex-column" style="font-size: 0.7rem;">
|
||||
<span class="text-success"><i class="fas fa-arrow-down opacity-50"></i> {{(item.stats.rx_bytes / 1048576) | number:'1.2-2'}} MB</span>
|
||||
<span class="text-info"><i class="fas fa-arrow-up opacity-50"></i> {{(item.stats.tx_bytes / 1048576) | number:'1.2-2'}} MB</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<c-form-check [switch]="true" class="d-inline-block">
|
||||
<input cFormCheckInput type="checkbox" [checked]="item.is_enabled"
|
||||
(click)="$event.preventDefault(); promptToggleEnabled(item)"
|
||||
[disabled]="item.is_managed === false" [id]="'switch-peer-' + item.id" />
|
||||
<label cFormCheckLabel [for]="'switch-peer-' + item.id"
|
||||
[ngClass]="item.is_enabled ? 'text-success fw-bold' : 'text-secondary'"
|
||||
style="cursor: pointer; font-size: 0.85rem;">
|
||||
{{ item.is_enabled ? 'Enabled' : 'Disabled' }}
|
||||
</label>
|
||||
[disabled]="item.is_managed === false" [id]="'switch-peer-' + item.public_key" />
|
||||
</c-form-check>
|
||||
</div>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button color="primary" shape="rounded-0" variant="ghost" style="padding: 4px 7px;"
|
||||
[matMenuTriggerFor]="menu" cButton>
|
||||
<i class="fa-solid fa-bars"></i>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<div cListGroup>
|
||||
<button size="sm" cListGroupItem (click)="openEditModal(item)" *ngIf="item.is_managed !== false">
|
||||
<i class="fa-solid fa-pencil text-primary"></i><small> Edit</small>
|
||||
</button>
|
||||
<button *ngIf="item.mt_user && !item.linked_device_id" size="sm" cListGroupItem
|
||||
(click)="scanDevice(item)">
|
||||
<i class="fa-solid fa-magnifying-glass text-info"></i><small> Manual Scan</small>
|
||||
</button>
|
||||
<button size="sm" cListGroupItem (click)="openScriptModal(item)">
|
||||
<i class="fa-solid fa-terminal text-secondary"></i><small> MikroTik Script</small>
|
||||
</button>
|
||||
<button size="sm" cListGroupItem (click)="openQrModal(item)">
|
||||
<i class="fa-solid fa-qrcode text-secondary"></i><small> QR Code</small>
|
||||
</button>
|
||||
<button size="sm" cListGroupItem (click)="downloadPeerConfigDirect(item)">
|
||||
<i class="fa-solid fa-download text-secondary"></i><small> Download Config</small>
|
||||
</button>
|
||||
<button size="sm" cListGroupItem (click)="promptResetPeer(item)">
|
||||
<i class="fa-solid fa-sync text-warning"></i><small> Reset Counters</small>
|
||||
</button>
|
||||
<button size="sm" cListGroupItem (click)="confirmDelete(item)">
|
||||
<i class="fa-solid fa-trash text-danger"></i><small> Delete</small>
|
||||
</button>
|
||||
</div>
|
||||
</mat-menu>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<gui-grid-column align="center" header="" field="_search_index" [enabled]="false" [width]="0">
|
||||
<ng-template let-item="item"></ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
<gui-grid-column align="center" [width]="80" [cellEditing]="false" [sorting]="false" header="Actions">
|
||||
<ng-template let-item="item">
|
||||
<button color="primary" shape="rounded-0" variant="ghost" style="padding: 4px 7px;"
|
||||
[matMenuTriggerFor]="menu" cButton>
|
||||
<i class="fa-solid fa-bars"></i>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<div cListGroup>
|
||||
<button size="sm" cListGroupItem (click)="openEditModal(item)" *ngIf="item.is_managed !== false">
|
||||
<i class="fa-solid fa-pencil text-primary"></i><small> Edit</small>
|
||||
</button>
|
||||
<button *ngIf="item.mt_user && !item.linked_device_id" size="sm" cListGroupItem
|
||||
(click)="scanDevice(item)">
|
||||
<i class="fa-solid fa-magnifying-glass text-info"></i><small> Manual Scan</small>
|
||||
</button>
|
||||
<button size="sm" cListGroupItem (click)="openScriptModal(item)">
|
||||
<i class="fa-solid fa-terminal text-secondary"></i><small> MikroTik Script</small>
|
||||
</button>
|
||||
<button size="sm" cListGroupItem (click)="openQrModal(item)">
|
||||
<i class="fa-solid fa-qrcode text-secondary"></i><small> QR Code</small>
|
||||
</button>
|
||||
<button size="sm" cListGroupItem (click)="downloadPeerConfigDirect(item)">
|
||||
<i class="fa-solid fa-download text-secondary"></i><small> Download Config</small>
|
||||
</button>
|
||||
<button size="sm" cListGroupItem (click)="promptResetPeer(item)">
|
||||
<i class="fa-solid fa-sync text-warning"></i><small> Reset Counters</small>
|
||||
</button>
|
||||
<button size="sm" cListGroupItem (click)="confirmDelete(item)">
|
||||
<i class="fa-solid fa-trash text-danger"></i><small> Delete</small>
|
||||
</button>
|
||||
</div>
|
||||
</mat-menu>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
</gui-grid>
|
||||
<ng-template pTemplate="emptymessage">
|
||||
<tr>
|
||||
<td colspan="10" class="text-center p-4">No VPN peers found.</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue