mikrofront/src/app/views/sequences/sequences.component.html

712 lines
No EOL
40 KiB
HTML

<div style="position: relative;">
<c-row>
<c-col xs>
<c-card class="mb-4">
<c-card-header>
<c-row>
<c-col xs [lg]="3">
Sequences
</c-col>
<c-col xs [lg]="9">
<h6 style="text-align: right;">
<button cButton color="dark" class="mx-1" size="sm" (click)="Edit_Sequence('','add')"
style="color: #fff;"><i class="fa-solid fa-plus"></i> Create Sequence</button>
<button cButton color="warning" class="mx-1" size="sm"
(click)="ManageAlertsModalVisible = true" style="color: #000;"><i
class="fa-solid fa-bell"></i> Manage Alerts</button>
</h6>
</c-col>
</c-row>
</c-card-header>
<c-card-body>
<div class="mb-3 d-flex justify-content-end">
<span class="p-input-icon-left table-search-input">
<i class="pi pi-search"></i>
<input type="text" pInputText placeholder="Search sequences..."
(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', 'created']" [loading]="false">
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="name" pResizableColumn>
<div class="d-flex justify-content-between align-items-center">
<span>Name</span>
<span>
<p-sortIcon field="name"></p-sortIcon>
<p-columnFilter type="text" field="name" display="menu" class="ms-auto" />
</span>
</div>
</th>
<th pSortableColumn="source_snippet_id" pResizableColumn>
<div class="d-flex justify-content-between align-items-center">
<span>Source Snippet</span>
<span>
<p-sortIcon field="source_snippet_id"></p-sortIcon>
<p-columnFilter type="numeric" field="source_snippet_id" display="menu"
class="ms-auto" />
</span>
</div>
</th>
<th pSortableColumn="is_active" class="text-center" pResizableColumn>
<div class="d-flex justify-content-between align-items-center">
<span>Active</span>
<span>
<p-sortIcon field="is_active"></p-sortIcon>
<p-columnFilter type="boolean" field="is_active" display="menu"
class="ms-auto" />
</span>
</div>
</th>
<th pSortableColumn="created" pResizableColumn>
<div class="d-flex justify-content-between align-items-center">
<span>Created</span>
<span>
<p-sortIcon field="created"></p-sortIcon>
<p-columnFilter type="text" field="created" display="menu" class="ms-auto" />
</span>
</div>
</th>
<th style="width: 280px" class="text-center" pResizableColumn>Actions</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-item>
<tr>
<td><strong>{{item.name}}</strong></td>
<td>Snippet #{{item.source_snippet_id}}</td>
<td class="text-center">
<i *ngIf="item.is_active" class="fa-solid fa-check" style="color: green;"></i>
<i *ngIf="!item.is_active" class="fa-solid fa-x" style="color: red;"></i>
</td>
<td>{{item.created}}</td>
<td class="text-center">
<div class="d-flex gap-1 justify-content-center">
<button cButton color="success" size="sm" (click)="Run_Sequence(item)"
pTooltip="Execute Sequence">
<i class="fa-solid fa-play"></i>
</button>
<button cButton color="primary" size="sm" (click)="Edit_Sequence(item,'edit')"
pTooltip="Edit Sequence">
<i class="fa-regular fa-pen-to-square"></i>
</button>
<button cButton color="info" size="sm" (click)="show_history(item)"
pTooltip="Execution History">
<i class="fa-solid fa-clock-rotate-left"></i>
</button>
<button cButton color="danger" size="sm" (click)="confirm_delete(item,false)"
pTooltip="Delete Sequence">
<i class="fa-regular fa-trash-can"></i>
</button>
</div>
</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td colspan="5" class="text-center p-4">No sequences found.</td>
</tr>
</ng-template>
</p-table>
</c-card-body>
</c-card>
</c-col>
</c-row>
<app-license-expired-overlay></app-license-expired-overlay>
</div>
<c-modal #EditSequenceModal backdrop="static" size="xl" [(visible)]="EditSequenceModalVisible" id="EditSequenceModal"
class="builder-modal">
<c-modal-header class="bg-light">
<h5 *ngIf="ModalAction=='edit'" cModalTitle>
<i class="fa-solid fa-edit me-2"></i>Edit Sequence: {{current_sequence['name']}}
</h5>
<h5 *ngIf="ModalAction=='add'" cModalTitle>
<i class="fa-solid fa-plus me-2"></i>Create New Sequence
</h5>
<button [cModalToggle]="EditSequenceModal.id" cButtonClose></button>
</c-modal-header>
<c-modal-body class="p-4 bg-light">
<!-- Basic Information -->
<c-card class="mb-4 shadow-sm border-0">
<c-card-header class="bg-white border-bottom-0 pt-3 pb-0">
<h6 class="mb-1"><i class="fa-solid fa-info-circle text-primary me-2"></i>Sequence Details</h6>
</c-card-header>
<c-card-body>
<c-row class="g-3 align-items-center">
<c-col xs="12" md="6">
<label class="form-label text-muted small mb-1">Sequence Name</label>
<input cFormControl placeholder="e.g. Daily Health Check" [(ngModel)]="current_sequence['name']"
class="border-secondary" />
</c-col>
<c-col xs="12" md="6">
<label class="form-label text-muted small mb-1">Source Snippet (Entry Point)</label>
<ngx-super-select [dataSource]="Snippets" [options]="snippetOptions"
(selectionChanged)="onSelectSourceSnippet($event)"
[selectedItemValues]="[current_sequence['source_snippet_id']]"
class="styled border-secondary"></ngx-super-select>
</c-col>
</c-row>
<div class="mt-3 pt-3 border-top d-flex gap-4">
<c-form-check>
<input cFormCheckInput type="checkbox" id="isActiveSeq"
[(ngModel)]="current_sequence['is_active']" />
<label cFormCheckLabel for="isActiveSeq" class="user-select-none">Sequence Active</label>
</c-form-check>
<c-form-check>
<input cFormCheckInput type="checkbox" id="storeHistorySeq"
[(ngModel)]="current_sequence['store_all_history']" />
<label cFormCheckLabel for="storeHistorySeq" class="user-select-none">Store Execution
History</label>
</c-form-check>
</div>
</c-card-body>
</c-card>
<!-- Flowchart Builder -->
<c-card class="mb-3 shadow-sm border-0 flowchart-container">
<c-card-header class="bg-white d-flex justify-content-between align-items-center py-3">
<h6 class="mb-0"><i class="fa-solid fa-project-diagram text-primary me-2"></i>Condition Flowchart</h6>
<button cButton color="primary" variant="outline" size="sm"
(click)="addCondition(current_sequence.conditions_json)">
<i class="fa-solid fa-plus me-1"></i>Add Root Condition
</button>
</c-card-header>
<c-card-body class="bg-light p-4 overflow-auto bg-grid-pattern">
<div *ngIf="!current_sequence.conditions_json || current_sequence.conditions_json.length === 0"
class="text-center py-5 text-muted">
<i class="fa-solid fa-code-branch fa-3x mb-3 opacity-25"></i>
<h6>No conditions defined</h6>
<p class="small mb-0">The source snippet will execute unconditionally.<br />Add a condition below to
build branching logic based on the snippet's output.</p>
</div>
<div class="flow-tree"
*ngIf="current_sequence.conditions_json && current_sequence.conditions_json.length > 0">
<!-- Root Entry Node -->
<div class="flow-entry-node">
<div class="entry-bubble"><i class="fa-solid fa-play me-2"></i>Source Snippet Output</div>
<div class="flow-connector-down"></div>
</div>
<!-- Recursive Conditions Render -->
<ng-container
*ngTemplateOutlet="conditionNodeTemplate; context: { conditions: current_sequence.conditions_json }"></ng-container>
</div>
</c-card-body>
</c-card>
</c-modal-body>
<c-modal-footer class="bg-white border-top-0 pt-0 pb-3 pe-4">
<button cButton color="secondary" variant="ghost" [cModalToggle]="EditSequenceModal.id">Cancel</button>
<button cButton color="primary" class="px-4" (click)="save_sequence()"><i class="fa-solid fa-save me-2"></i>Save
Sequence</button>
</c-modal-footer>
</c-modal>
<!-- RECURSIVE TEMPLATE FOR CONDITIONS -->
<ng-template #conditionNodeTemplate let-conditions="conditions">
<div class="condition-group" *ngIf="conditions && conditions.length > 0">
<div class="condition-branch" *ngFor="let cond of conditions; let i = index">
<!-- Condition Card -->
<div class="node-card condition-node">
<div
class="node-header bg-warning bg-opacity-10 text-warning d-flex justify-content-between align-items-center">
<span><i class="fa-solid fa-code-compare me-2"></i>Condition</span>
<button class="btn btn-link text-danger p-0 m-0" (click)="removeNode(conditions, i)"><i
class="fa-solid fa-times"></i></button>
</div>
<div class="node-body">
<c-row class="g-2 align-items-center">
<c-col xs="12" md="12" class="mb-2">
<select class="form-select form-select-sm" [(ngModel)]="cond.type">
<option value="contains">Contains Content</option>
<option value="not_contain">Does Not Contain</option>
</select>
</c-col>
<c-col xs="12" md="12">
<div class="input-group input-group-sm">
<div class="input-group-text bg-white border-end-0 pe-1">
<input class="form-check-input mt-0" type="checkbox" [(ngModel)]="cond.is_regex"
title="Use Regex">
<small class="ms-1 text-muted" title="Use Regex">Regex</small>
</div>
<input type="text" class="form-control border-start-0 ps-1" [(ngModel)]="cond.pattern"
placeholder="Match string or pattern...">
</div>
</c-col>
</c-row>
<div class="node-footer mt-2 pt-2 border-top text-center">
<button class="btn btn-sm btn-outline-primary py-0" style="font-size: 0.75rem"
(click)="addAction(cond)">
<i class="fa-solid fa-plus me-1"></i>Add Action
</button>
</div>
</div>
</div>
<!-- Flow Connector from Condition to Actions -->
<div class="flow-connector-down" *ngIf="cond.actions && cond.actions.length > 0"></div>
<!-- Render Actions under this condition -->
<div class="action-group" *ngIf="cond.actions && cond.actions.length > 0">
<div class="action-branch" *ngFor="let act of cond.actions; let j = index">
<div class="node-card action-node">
<div
class="node-header bg-success bg-opacity-10 text-success d-flex justify-content-between align-items-center">
<span><i class="fa-solid fa-bolt me-2"></i>Action</span>
<button class="btn btn-link text-danger p-0 m-0" (click)="removeNode(cond.actions, j)"><i
class="fa-solid fa-times"></i></button>
</div>
<div class="node-body">
<select class="form-select form-select-sm mb-2" [(ngModel)]="act.action_type">
<option value="alert_set">Trigger Alert</option>
<option value="alert_clear">Clear Alert</option>
<option value="execute_snippet">Execute Another Snippet</option>
</select>
<!-- Alert Selection -->
<div *ngIf="act.action_type === 'alert_set' || act.action_type === 'alert_clear'">
<select class="form-select form-select-sm" [(ngModel)]="act.alert_id">
<option [ngValue]="null">Select an Alert Definition...</option>
<option *ngFor="let a of Alerts" [value]="a.id">{{a.name}} ({{a.level}})</option>
</select>
</div>
<!-- Snippet Selection -->
<div *ngIf="act.action_type === 'execute_snippet'">
<select class="form-select form-select-sm" [(ngModel)]="act.snippet_id">
<option [ngValue]="null">Select a Snippet to Run...</option>
<option *ngFor="let s of Snippets" [value]="s.id">{{s.name}}</option>
</select>
<!-- Recursive Conditions for nested Snippet -->
<div class="mt-2 pt-2 border-top text-center">
<button class="btn btn-sm btn-outline-warning py-0" style="font-size: 0.75rem"
(click)="addCondition(act.conditions || (act.conditions = []))">
<i class="fa-solid fa-code-compare me-1"></i>Add Nested Condition
</button>
</div>
</div>
</div>
</div>
<!-- Recursive render of nested conditions from Snippet Action -->
<div *ngIf="act.action_type === 'execute_snippet' && act.conditions && act.conditions.length > 0">
<div class="flow-connector-down"></div>
<ng-container
*ngTemplateOutlet="conditionNodeTemplate; context: { conditions: act.conditions }"></ng-container>
</div>
</div> <!-- end action-branch loop -->
</div> <!-- end action-group -->
</div> <!-- end condition-branch loop -->
</div> <!-- end condition-group -->
</ng-template>
<!-- Manage Alerts Modal -->
<c-modal #ManageAlertsModal backdrop="static" size="lg" [(visible)]="ManageAlertsModalVisible" id="ManageAlertsModal">
<c-modal-header class="bg-light">
<h5 cModalTitle><i class="fa-solid fa-bell me-2"></i>Manage Alert Definitions</h5>
<button [cModalToggle]="ManageAlertsModal.id" cButtonClose></button>
</c-modal-header>
<c-modal-body class="p-4 bg-light">
<c-row>
<!-- Form Column -->
<c-col xs="12" md="4" class="mb-4">
<c-card class="shadow-sm border-0">
<c-card-header class="bg-white">
<h6 class="mb-0">{{editingAlert.id === 0 ? 'Create Alert' : 'Edit Alert'}}</h6>
</c-card-header>
<c-card-body>
<div class="mb-3">
<label class="form-label small text-muted">Alert Name</label>
<input class="form-control" [(ngModel)]="editingAlert.name" placeholder="e.g. CPU High">
</div>
<div class="mb-3">
<label class="form-label small text-muted">Severity Level</label>
<select class="form-select" [(ngModel)]="editingAlert.level">
<option value="Warning">Warning</option>
<option value="Critical">Critical</option>
<option value="Error">Error</option>
</select>
</div>
<button cButton color="primary" class="w-100" (click)="saveAlert()">
<i class="fa-solid fa-save me-1"></i>Save Alert
</button>
<button *ngIf="editingAlert.id !== 0" cButton color="secondary" variant="ghost"
class="w-100 mt-2" (click)="editingAlert = { id: 0, name: '', level: 'info' }">Cancel
Edit</button>
</c-card-body>
</c-card>
</c-col>
<!-- List Column -->
<c-col xs="12" md="8">
<c-card class="shadow-sm border-0">
<c-card-header class="bg-white">
<h6 class="mb-0">Existing Alerts</h6>
</c-card-header>
<c-card-body class="p-0">
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle">
<thead class="bg-light">
<tr>
<th>Name</th>
<th>Level</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let alert of Alerts">
<td class="fw-semibold">{{alert.name}}</td>
<td>
<c-badge
[color]="alert.level === 'critical' ? 'danger' : (alert.level === 'warning' ? 'warning' : 'info')">
{{alert.level | uppercase}}
</c-badge>
</td>
<td class="text-end">
<button cButton color="info" size="sm" variant="ghost" class="me-1"
(click)="editAlert(alert)"><i class="fa-solid fa-edit"></i></button>
<button cButton color="danger" size="sm" variant="ghost"
(click)="deleteAlert(alert.id)"><i
class="fa-solid fa-trash"></i></button>
</td>
</tr>
<tr *ngIf="Alerts.length === 0">
<td colspan="3" class="text-center text-muted py-4">No alerts defined.</td>
</tr>
</tbody>
</table>
</div>
</c-card-body>
</c-card>
</c-col>
</c-row>
</c-modal-body>
</c-modal>
<!-- Execution History Modal -->
<c-modal #HistoryModal backdrop="static" size="xl" [(visible)]="HistoryModalVisible" id="HistoryModal">
<c-modal-header class="bg-light">
<h5 cModalTitle><i class="fa-solid fa-clock-rotate-left me-2"></i>Execution History: {{viewing_sequence_name}}
</h5>
<button [cModalToggle]="HistoryModal.id" cButtonClose></button>
</c-modal-header>
<c-modal-body class="p-4 bg-light">
<c-card class="shadow-sm border-0">
<c-card-body class="p-0">
<div *ngIf="sequence_history.length > 0">
<div *ngIf="sequence_history.length > 0">
<div *ngFor="let run of sequence_history; let i = index" class="mb-3">
<c-card class="border-0 shadow-sm overflow-hidden">
<c-card-header (click)="run.visible = !run.visible" style="cursor: pointer;"
class="d-flex justify-content-between align-items-center bg-white border-0 py-3">
<span>
<i class="fa-solid"
[ngClass]="run.visible ? 'fa-chevron-down' : 'fa-chevron-right'"
class="me-3 text-muted"></i>
<i class="fa-solid fa-calendar-check me-2 text-primary"></i>
<strong>{{run.created}}</strong>
<small class="text-muted ms-3">Exec ID: {{run.exec_id | slice:0:8}}...</small>
</span>
<div class="d-flex align-items-center">
<c-badge
[color]="run.successCount === run.totalCount ? 'success' : (run.successCount === 0 ? 'danger' : 'warning')"
class="px-3 py-2 text-white">
{{run.successCount}}/{{run.totalCount}} Success
</c-badge>
</div>
</c-card-header>
<div [visible]="run.visible" cCollapse class="border-top">
<c-card-body class="p-0 border-top">
<p-table [value]="run.devices" [paginator]="true" [rows]="5"
[showGridlines]="true" [stripedRows]="true" styleClass="p-datatable-sm"
class="border-0">
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="device_id">Device <p-sortIcon
field="device_id"></p-sortIcon></th>
<th pSortableColumn="status" class="text-center"
style="width: 150px">Status <p-sortIcon
field="status"></p-sortIcon></th>
<th style="width: 150px" class="text-end">Actions</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-dev>
<tr>
<td>
<div class="fw-semibold ms-2">
<i class="fa-solid fa-microchip me-2 opacity-50"></i>Device
#{{dev.device_id}}
</div>
</td>
<td class="text-center">
<c-badge
[color]="dev.status === 'success' ? 'success' : 'danger'"
class="text-white px-2">
{{dev.status | uppercase}}
</c-badge>
</td>
<td class="text-end">
<button cButton color="dark" size="sm" variant="outline"
pTooltip="Trace Log" (click)="showTrace(dev)">
<i class="fa-solid fa-terminal"></i>
</button>
</td>
</tr>
</ng-template>
</p-table>
</c-card-body>
</div>
</c-card>
</div>
</div>
</div>
<div *ngIf="sequence_history.length === 0" class="text-center text-muted py-5">
<i class="fa-solid fa-inbox fa-3x mb-3 opacity-25"></i>
<h6>No History Found</h6>
<p class="small mb-0">This sequence hasn't been executed yet or history tracking is disabled.</p>
</div>
</c-card-body>
</c-card>
</c-modal-body>
</c-modal>
<!-- Trace Log Modal (Terminal Style) -->
<c-modal #TraceModal backdrop="static" size="xl" [(visible)]="TraceModalVisible" id="TraceModal">
<c-modal-header class="bg-dark text-white">
<h5 cModalTitle><i class="fa-solid fa-terminal me-2"></i>Execution Trace: Device #{{viewing_device_id}}</h5>
<button (click)="TraceModalVisible=false" cButtonClose white></button>
</c-modal-header>
<c-modal-body class="p-0 bg-dark">
<div class="nav nav-tabs border-secondary px-3 pt-2">
<div class="nav-item">
<a [cTabContent]="tabContent" [active]="true" [tabPaneIdx]="0"
class="nav-link text-light border-secondary cursor-pointer">
<i class="fa-solid fa-code me-2"></i>SSH Output
</a>
</div>
<div class="nav-item">
<a [cTabContent]="tabContent" [active]="false" [tabPaneIdx]="1"
class="nav-link text-light border-secondary cursor-pointer">
<i class="fa-solid fa-list-check me-2"></i>Evaluation Details
</a>
</div>
</div>
<c-tab-content #tabContent="cTabContent" class="p-3">
<c-tab-pane class="p-0">
<div class="terminal-container">
<div class="terminal-content">
<div highlight-js lang="routeros" [options]="{}">{{
current_trace.parsedLog?.ssh_output || current_trace.task_log }}</div>
</div>
</div>
</c-tab-pane>
<c-tab-pane class="p-3 text-light">
<div class="evaluation-tree" *ngIf="current_trace.parsedLog?.evaluation">
<!-- Structured JSON Evaluation -->
<div *ngIf="isObject(current_trace.parsedLog.evaluation); else flatEval">
<ng-container
*ngTemplateOutlet="evalNodeTemplate; context: { node: current_trace.parsedLog.evaluation }"></ng-container>
</div>
<!-- Legacy String Evaluation -->
<ng-template #flatEval>
<pre class="text-info bg-transparent border-0 p-0 m-0"
style="white-space: pre-wrap;">{{current_trace.parsedLog.evaluation}}</pre>
</ng-template>
</div>
<div *ngIf="!current_trace.parsedLog?.evaluation" class="text-center py-4 text-muted">
No evaluation details found for this run.
</div>
</c-tab-pane>
</c-tab-content>
</c-modal-body>
<c-modal-footer class="bg-dark border-secondary">
<button cButton color="secondary" (click)="TraceModalVisible=false">Close Trace</button>
</c-modal-footer>
</c-modal>
<!-- RECURSIVE TEMPLATE FOR EVALUATION LOGS -->
<ng-template #evalNodeTemplate let-node="node">
<div class="eval-node ms-3 border-start border-secondary ps-3 mb-3">
<div *ngFor="let rule of (isArray(node) ? node : [node])" class="mb-2">
<div class="d-flex align-items-center mb-1">
<i class="fa-solid fa-check-circle text-success me-2" *ngIf="rule.matched"></i>
<i class="fa-regular fa-circle text-muted me-2" *ngIf="!rule.matched"></i>
<span [class.text-white]="rule.matched" [class.text-muted]="!rule.matched">
Rule <strong>[{{rule.type}}]</strong>: '{{rule.pattern}}' &rarr;
<span [class.text-success]="rule.matched">{{rule.matched ? 'MATCHED' : 'NO MATCH'}}</span>
</span>
</div>
<div *ngIf="rule.matched && rule.actions?.length > 0" class="ms-4 my-2 p-2 bg-opacity-10 bg-info rounded">
<div *ngFor="let act of rule.actions" class="mb-1 small">
<i class="fa-solid fa-arrow-right text-info me-2"></i>
<strong>ACTION:</strong> {{act.action_type}}
<span *ngIf="act.alert_id" class="text-warning ms-1">(Alert ID: {{act.alert_id}})</span>
<span *ngIf="act.snippet_id" class="text-primary ms-1">(Snippet: {{act.snippet_id}})</span>
<!-- Nested Evaluation if another snippet was run -->
<div *ngIf="act.nested_eval" class="mt-2">
<ng-container
*ngTemplateOutlet="evalNodeTemplate; context: { node: act.nested_eval }"></ng-container>
</div>
</div>
</div>
</div>
</div>
</ng-template>
<!-- MANUAL EXECUTION MODAL -->
<c-modal #ExecSequenceModal backdrop="static" size="xl" [(visible)]="ExecSequenceModalVisible" id="ExecSequenceModal">
<c-modal-header>
<h5 cModalTitle>Exec Sequence</h5>
<button [cModalToggle]="ExecSequenceModal.id" cButtonClose></button>
</c-modal-header>
<c-modal-body>
<div [cFormFloating]="true" class="mb-3">
<input cFormControl id="floatingExecName" placeholder="current_sequence['name']"
[(ngModel)]="current_sequence['name']" disabled="true" />
<label cLabel for="floatingExecName">Sequence Name</label>
</div>
<div [cFormFloating]="true" class="mb-3">
<input cFormControl id="floatingExecDesc" placeholder="Description"
[(ngModel)]="current_sequence['description']" />
<label cLabel for="floatingExecDesc">Description</label>
</div>
<c-input-group class="mb-3">
<label cInputGroupText for="inputGroupSelectSeq">
Member type
</label>
<select cSelect id="inputGroupSelectSeq" (change)="form_changed()"
[(ngModel)]="current_sequence['selection_type']">
<option value="devices">Devices</option>
<option value="groups">Groups</option>
</select>
</c-input-group>
<h5>Members :</h5>
<p-table #dtMembers [value]="SelectedMembers" [paginator]="true" [rows]="10" [showGridlines]="true"
[stripedRows]="true" styleClass="p-datatable-sm">
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="name">Name <p-sortIcon field="name"></p-sortIcon></th>
<th *ngIf="current_sequence['selection_type']=='devices'" pSortableColumn="mac">MAC <p-sortIcon
field="mac"></p-sortIcon></th>
<th style="width: 100px" class="text-center">Actions</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-item>
<tr>
<td>{{item.name}}</td>
<td *ngIf="current_sequence['selection_type']=='devices'">{{item.mac}}</td>
<td class="text-center">
<button cButton color="danger" size="sm" (click)="remove_member(item)" pTooltip="Remove Member">
<i class="fa-regular fa-trash-can"></i>
</button>
</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td [attr.colspan]="current_sequence['selection_type']=='devices' ? 3 : 2" class="text-center p-2">
No members added.</td>
</tr>
</ng-template>
</p-table>
<hr />
<button cButton color="primary" (click)="show_new_member_form()">+ Add new Members</button>
</c-modal-body>
<c-modal-footer>
<button (click)="submit_exec()" cButton color="primary">Execute</button>
<button [cModalToggle]="ExecSequenceModal.id" cButton color="secondary">
Close
</button>
</c-modal-footer>
</c-modal>
<!-- MEMBER SELECTION MODAL -->
<c-modal #NewMemberModal backdrop="static" size="lg" [(visible)]="NewMemberModalVisible" id="NewMemberModal">
<c-modal-header>
<h5 cModalTitle>Editing Group </h5>
<button (click)="NewMemberModalVisible=!NewMemberModalVisible" cButtonClose></button>
</c-modal-header>
<c-modal-body>
<c-input-group class="mb-3">
<h5>Group Members :</h5>
<div class="mb-3 d-flex justify-content-end w-100">
<span class="p-input-icon-left table-search-input">
<i class="pi pi-search"></i>
<input type="text" pInputText placeholder="Search members..."
(input)="applyFilterNewMembers($event, 'contains')" class="form-control-sm" />
</span>
</div>
<p-table #dtNewMembers [value]="availbleMembers" [paginator]="true" [rows]="10" [showGridlines]="true"
[stripedRows]="true" styleClass="p-datatable-sm" [(selection)]="SelectedNewMemberRows"
(selectionChange)="onSelectedRowsNewMembers($event)" [globalFilterFields]="['name', 'ip', 'mac']">
<ng-template pTemplate="header">
<tr>
<th style="width: 4rem" pResizableColumn><p-tableHeaderCheckbox></p-tableHeaderCheckbox></th>
<th pSortableColumn="name" pResizableColumn>
<div class="d-flex justify-content-between align-items-center">
<span>Member Name</span>
<p-sortIcon field="name"></p-sortIcon>
</div>
</th>
<th *ngIf="current_sequence['selection_type']=='devices'" pSortableColumn="ip" pResizableColumn>
<div class="d-flex justify-content-between align-items-center">
<span>IP Address</span>
<p-sortIcon field="ip"></p-sortIcon>
</div>
</th>
<th *ngIf="current_sequence['selection_type']=='devices'" pSortableColumn="mac"
pResizableColumn>
<div class="d-flex justify-content-between align-items-center">
<span>MAC Address</span>
<p-sortIcon field="mac"></p-sortIcon>
</div>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-item>
<tr>
<td><p-tableCheckbox [value]="item"></p-tableCheckbox></td>
<td>{{item.name}}</td>
<td *ngIf="current_sequence['selection_type']=='devices'">{{item.ip}}</td>
<td *ngIf="current_sequence['selection_type']=='devices'">{{item.mac}}</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td [attr.colspan]="current_sequence['selection_type']=='devices' ? 4 : 2"
class="text-center p-4">No available members found.</td>
</tr>
</ng-template>
</p-table>
<br />
</c-input-group>
<hr />
</c-modal-body>
<c-modal-footer>
<button (click)="add_new_members()" cButton color="primary">Add Selected</button>
<button (click)="NewMemberModalVisible=false" cButton color="secondary">
Close
</button>
</c-modal-footer>
</c-modal>