mirror of
https://github.com/MikroWizard/mikrofront.git
synced 2025-12-06 10:09:29 +00:00
feat: redesign sync and cloner interface for pro users
- Complete UI/UX overhaul of cloner module - Enhanced configuration synchronization - Pro feature improvements - Better cloning workflow and interface
This commit is contained in:
parent
2f68c49936
commit
cdc2e0cabf
5 changed files with 783 additions and 136 deletions
494
src/app/views/cloner/cloner-styles.scss
Normal file
494
src/app/views/cloner/cloner-styles.scss
Normal file
|
|
@ -0,0 +1,494 @@
|
||||||
|
/* Modern Cloner Component Styles */
|
||||||
|
|
||||||
|
/* Form Sections */
|
||||||
|
.cloner-form-section {
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
border-bottom: 1px solid #e9ecef;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
color: #495057;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input-sm {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
padding: 0.375rem 0.5rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
height: calc(1.5em + 0.75rem + 2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:focus {
|
||||||
|
border-color: #0d6efd;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label-sm {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #6c757d;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label-xs {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #6c757d;
|
||||||
|
margin-bottom: 0.125rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select-sm {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
height: calc(1.8em + 0.75rem + 2px);
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select-xs {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
height: calc(1.5em + 0.75rem + 2px);
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Commands Configuration */
|
||||||
|
.commands-container {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-container-compact {
|
||||||
|
background: white;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-nav {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-bottom: 2px solid #e9ecef;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-nav-compact {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-bottom: 1px solid #e9ecef;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-nav .nav-item {
|
||||||
|
margin-bottom: -2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-nav .nav-link {
|
||||||
|
color: #6c757d;
|
||||||
|
border-style: none none solid;
|
||||||
|
border-width: 2px;
|
||||||
|
position: relative;
|
||||||
|
bottom: -1px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-nav .nav-link:hover,
|
||||||
|
.commands-nav .nav-link:focus {
|
||||||
|
border-color: #0d6efd;
|
||||||
|
color: #0d6efd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-nav .nav-link.active {
|
||||||
|
color: #0d6efd;
|
||||||
|
background: transparent;
|
||||||
|
border-color: #0d6efd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-sections {
|
||||||
|
padding: 1rem;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-sections-compact {
|
||||||
|
padding: 0.5rem;
|
||||||
|
min-height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-category {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-category-compact {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-title {
|
||||||
|
color: #0d6efd;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom: 1px solid #e9ecef;
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-title-compact {
|
||||||
|
color: #0d6efd;
|
||||||
|
margin-bottom: 0.375rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom: 1px solid #e9ecef;
|
||||||
|
padding-bottom: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-grid-compact {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item-compact {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item:hover {
|
||||||
|
border-color: #0d6efd;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-content-compact {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-name {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #495057;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-name-compact {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #495057;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Switch Styling */
|
||||||
|
.custom-switch {
|
||||||
|
position: relative;
|
||||||
|
width: 40px;
|
||||||
|
height: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-switch-compact {
|
||||||
|
position: relative;
|
||||||
|
width: 32px;
|
||||||
|
height: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-switch-compact input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-switch-compact .custom-control-label {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-switch-compact .custom-control-label::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 2px;
|
||||||
|
top: 2px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-switch-compact input:checked + .custom-control-label {
|
||||||
|
background: #0d6efd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-switch-compact input:checked + .custom-control-label::after {
|
||||||
|
left: calc(100% - 14px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-switch input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-control-label {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 20px;
|
||||||
|
background: #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-control-label::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 2px;
|
||||||
|
top: 2px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-switch input:checked + .custom-control-label {
|
||||||
|
background: #0d6efd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-switch input:checked + .custom-control-label::after {
|
||||||
|
left: calc(100% - 18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Master Device Selection */
|
||||||
|
.master-selection {
|
||||||
|
background: white;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.master-selection-compact {
|
||||||
|
background: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.master-device {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: #fff3cd;
|
||||||
|
border: 1px solid #ffeaa7;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.master-device-compact {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.375rem;
|
||||||
|
background: #fff3cd;
|
||||||
|
border: 1px solid #ffeaa7;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.master-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-master {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: #f8d7da;
|
||||||
|
border: 1px solid #f5c6cb;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-master-compact {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.375rem;
|
||||||
|
background: #f8d7da;
|
||||||
|
border: 1px solid #f5c6cb;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Peers Container */
|
||||||
|
.peers-container {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-peers {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text strong {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-members-section {
|
||||||
|
padding: 1rem;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-top: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal Enhancements */
|
||||||
|
.c-modal-header.bg-light {
|
||||||
|
background: #f8f9fa !important;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-modal-header.bg-success {
|
||||||
|
background: #198754 !important;
|
||||||
|
border-bottom: 1px solid #146c43;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-modal-footer.bg-light {
|
||||||
|
background: #f8f9fa !important;
|
||||||
|
border-top: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-modal-body {
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button Groups */
|
||||||
|
.btn-group .btn {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group .btn:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.cloner-form-section {
|
||||||
|
padding: 0.75rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-content {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-name {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-modal-dialog {
|
||||||
|
margin: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands-nav .nav-link {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.master-device,
|
||||||
|
.no-master {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.c-modal-footer {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-modal-footer > div {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-modal-footer > div:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,17 +15,27 @@
|
||||||
<c-card-body>
|
<c-card-body>
|
||||||
<gui-grid [autoResizeWidth]="true" [source]="source" [columnMenu]="columnMenu" [sorting]="sorting"
|
<gui-grid [autoResizeWidth]="true" [source]="source" [columnMenu]="columnMenu" [sorting]="sorting"
|
||||||
[infoPanel]="infoPanel" [autoResizeWidth]=true>
|
[infoPanel]="infoPanel" [autoResizeWidth]=true>
|
||||||
|
<gui-grid-column header="Name" field="name">
|
||||||
|
<ng-template let-value="item.name" let-item="item" let-index="index">
|
||||||
|
<strong>{{value}}</strong>
|
||||||
|
</ng-template>
|
||||||
|
</gui-grid-column>
|
||||||
<gui-grid-column header="Description" field="description">
|
<gui-grid-column header="Description" field="description">
|
||||||
<ng-template let-value="item.description" let-item="item" let-index="index">
|
<ng-template let-value="item.description" let-item="item" let-index="index">
|
||||||
{{value}}
|
{{value}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</gui-grid-column>
|
</gui-grid-column>
|
||||||
<gui-grid-column header="Members type" field="pair_type">
|
<gui-grid-column header="Direction" field="direction">
|
||||||
<ng-template let-value="item.pair_type" let-item="item" let-index="index">
|
<ng-template let-value="item.direction" let-item="item" let-index="index">
|
||||||
{{value}}
|
<c-badge [color]="value == 'twoway' ? 'success' : 'warning'">{{value == 'twoway' ? 'Two Way' : 'Master Mode'}}</c-badge>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</gui-grid-column>
|
</gui-grid-column>
|
||||||
<gui-grid-column header="Runtime" field="desc_cron">
|
<gui-grid-column header="Live Mode" field="live_mode">
|
||||||
|
<ng-template let-value="item.live_mode" let-item="item" let-index="index">
|
||||||
|
<c-badge [color]="value ? 'success' : 'secondary'">{{value ? 'Active' : 'Inactive'}}</c-badge>
|
||||||
|
</ng-template>
|
||||||
|
</gui-grid-column>
|
||||||
|
<gui-grid-column header="Schedule" field="desc_cron">
|
||||||
<ng-template let-value="item.desc_cron" let-item="item" let-index="index">
|
<ng-template let-value="item.desc_cron" let-item="item" let-index="index">
|
||||||
{{value}}
|
{{value}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
@ -46,109 +56,190 @@
|
||||||
</c-col>
|
</c-col>
|
||||||
</c-row>
|
</c-row>
|
||||||
|
|
||||||
<c-modal #EditClonerModal backdrop="static" size="xl" [(visible)]="EditClonerModalVisible" id="EditClonerModal">
|
<c-modal #EditClonerModal backdrop="static" size="lg" [(visible)]="EditClonerModalVisible" id="EditClonerModal">
|
||||||
<c-modal-header>
|
<c-modal-header class="bg-light">
|
||||||
<h5 *ngIf="SelectedCloner['action']=='edit'" cModalTitle>Editing Cloner {{SelectedCloner['name']}}</h5>
|
<h5 *ngIf="SelectedCloner['action']=='edit'" cModalTitle><i class="fa-solid fa-edit me-2"></i>Edit Cloner: {{SelectedCloner['name']}}</h5>
|
||||||
<h5 *ngIf="SelectedCloner['action']=='add'" cModalTitle>Adding new task</h5>
|
<h5 *ngIf="SelectedCloner['action']=='add'" cModalTitle><i class="fa-solid fa-plus me-2"></i>Add New Cloner</h5>
|
||||||
<button [cModalToggle]="EditClonerModal.id" cButtonClose></button>
|
<button [cModalToggle]="EditClonerModal.id" cButtonClose></button>
|
||||||
</c-modal-header>
|
</c-modal-header>
|
||||||
<c-modal-body>
|
<c-modal-body class="p-3">
|
||||||
<label cLabel >General data:</label>
|
<!-- Basic Information -->
|
||||||
<c-input-group class="mb-3">
|
<div class="cloner-form-section mb-3">
|
||||||
<label cInputGroupText for="floatingInput">Name</label>
|
<div class="section-header mb-2">
|
||||||
<input cFormControl id="floatingInput" placeholder="SelectedCloner['name']" [(ngModel)]="SelectedCloner['name']" />
|
<h6 class="section-title mb-0"><i class="fa-solid fa-info-circle me-2"></i>Basic Information</h6>
|
||||||
<label cInputGroupText for="floatingInput">Description</label>
|
|
||||||
<input cFormControl id="floatingInput" placeholder="SelectedCloner['description']"
|
|
||||||
[(ngModel)]="SelectedCloner['description']" />
|
|
||||||
</c-input-group>
|
|
||||||
<label cLabel >Sync config and Oprating Modes:</label>
|
|
||||||
<c-input-group class="mb-3">
|
|
||||||
<label cInputGroupText for="Direction">
|
|
||||||
Direction
|
|
||||||
</label>
|
|
||||||
<select cSelect id="Direction" [(ngModel)]="SelectedCloner['direction']">
|
|
||||||
<option value="twoway">Two way</option>
|
|
||||||
<option value="oneway">Master mode</option>
|
|
||||||
</select>
|
|
||||||
<label cInputGroupText for="inputGroupSelect01">
|
|
||||||
Live Mode
|
|
||||||
</label>
|
|
||||||
<select cSelect id="inputGroupSelect01" [(ngModel)]="SelectedCloner['live_mode']">
|
|
||||||
<option [ngValue]="false">Deactive</option>
|
|
||||||
<option [ngValue]="true">Active</option>
|
|
||||||
</select>
|
|
||||||
<label *ngIf="SelectedCloner['direction']=='oneway'" cInputGroupText for="inputGroupSelect02">
|
|
||||||
Schedule
|
|
||||||
</label>
|
|
||||||
<select *ngIf="SelectedCloner['direction']=='oneway'" cSelect id="inputGroupSelect02" [(ngModel)]="SelectedCloner['schedule']">
|
|
||||||
<option [ngValue]="false">Deactive</option>
|
|
||||||
<option [ngValue]="true">Active</option>
|
|
||||||
</select>
|
|
||||||
<label cInputGroupText *ngIf="SelectedCloner['schedule'] && SelectedCloner['direction']=='oneway'" for="cron">cron</label>
|
|
||||||
<input cFormControl *ngIf="SelectedCloner['schedule'] && SelectedCloner['direction']=='oneway'" id="cron" placeholder="Cron" [(ngModel)]="SelectedCloner['cron']" />
|
|
||||||
</c-input-group>
|
|
||||||
<label cLabel >Peers Setting:</label>
|
|
||||||
<c-input-group class="mb-3">
|
|
||||||
<label cInputGroupText for="inputGroupSelect03">
|
|
||||||
Peers type
|
|
||||||
</label>
|
|
||||||
<select cSelect id="inputGroupSelect03" (change)="form_changed()" [(ngModel)]="SelectedCloner['pair_type']">
|
|
||||||
<option value="devices">Devices</option>
|
|
||||||
<option value="groups" *ngIf="SelectedCloner['direction']=='oneway'">Groups</option>
|
|
||||||
</select>
|
|
||||||
</c-input-group>
|
|
||||||
|
|
||||||
<c-col xs style="border: 1px solid #ddd; border-radius: 4px; padding: 0;">
|
|
||||||
<div class="nav nav-underline" style="background: #fff; border-bottom: 2px solid #2c3e50;">
|
|
||||||
<div class="nav-item" *ngFor="let tab of tabs; let i = index">
|
|
||||||
<a class="nav-link" [active]="i==0" [cTabContent]="tabContent" [tabPaneIdx]="i">{{ tab.name }}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<c-tab-content class="command-sections" style="padding: 10px!important;min-height: 150px;" #tabContent="cTabContent">
|
<c-row class="g-2">
|
||||||
<c-tab-pane *ngFor="let tab of tabs; let i = index">
|
<c-col xs="12" md="6">
|
||||||
<div class="section" *ngFor="let section of tab.sections">
|
<input cFormControl placeholder="Cloner Name" [(ngModel)]="SelectedCloner['name']" class="form-input-sm" />
|
||||||
<h5 class="cloner-sections">{{ section.title }}</h5>
|
</c-col>
|
||||||
<div class="row">
|
<c-col xs="12" md="6">
|
||||||
<div class="col-4" *ngFor="let command of section.commands">
|
<input cFormControl placeholder="Description" [(ngModel)]="SelectedCloner['description']" class="form-input-sm" />
|
||||||
<c-card style="margin-bottom: 5px;">
|
</c-col>
|
||||||
<c-card-body class="p-2">
|
</c-row>
|
||||||
<h6 class="card-title mb-1">{{ command }}</h6>
|
</div>
|
||||||
<div class="custom-switch">
|
|
||||||
|
<!-- Sync Configuration -->
|
||||||
|
<div class="cloner-form-section mb-3">
|
||||||
|
<div class="section-header mb-2">
|
||||||
|
<h6 class="section-title mb-0"><i class="fa-solid fa-sync me-2"></i>Synchronization Settings</h6>
|
||||||
|
</div>
|
||||||
|
<c-row class="g-2">
|
||||||
|
<c-col xs="12" md="4">
|
||||||
|
<label class="form-label-xs">Direction</label>
|
||||||
|
<select cSelect [(ngModel)]="SelectedCloner['direction']" (change)="onDirectionChange()" class="form-select-xs">
|
||||||
|
<option value="twoway">Two Way Sync</option>
|
||||||
|
<option value="oneway">Master Mode</option>
|
||||||
|
</select>
|
||||||
|
</c-col>
|
||||||
|
<c-col xs="12" md="4" *ngIf="SelectedCloner['direction']=='oneway'">
|
||||||
|
<label class="form-label-xs">Live Mode</label>
|
||||||
|
<select cSelect [(ngModel)]="SelectedCloner['live_mode']" class="form-select-xs">
|
||||||
|
<option [ngValue]="false">Inactive</option>
|
||||||
|
<option [ngValue]="true">Active</option>
|
||||||
|
</select>
|
||||||
|
</c-col>
|
||||||
|
<c-col xs="12" md="4" *ngIf="SelectedCloner['direction']=='oneway'">
|
||||||
|
<label class="form-label-xs">Schedule</label>
|
||||||
|
<select cSelect [(ngModel)]="SelectedCloner['schedule']" class="form-select-xs">
|
||||||
|
<option [ngValue]="false">Inactive</option>
|
||||||
|
<option [ngValue]="true">Active</option>
|
||||||
|
</select>
|
||||||
|
</c-col>
|
||||||
|
<c-col xs="12" *ngIf="SelectedCloner['schedule'] && SelectedCloner['direction']=='oneway'">
|
||||||
|
<label class="form-label-xs">Cron Expression</label>
|
||||||
|
<input cFormControl placeholder="0 0 * * *" [(ngModel)]="SelectedCloner['cron']" class="form-input-sm" />
|
||||||
|
</c-col>
|
||||||
|
</c-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Peers Configuration (Currently disabled - only devices supported) -->
|
||||||
|
<!-- <div class="cloner-form-section mb-3">
|
||||||
|
<div class="section-header mb-2">
|
||||||
|
<h6 class="section-title mb-0"><i class="fa-solid fa-network-wired me-2"></i>Peers Configuration</h6>
|
||||||
|
<small class="text-muted">Select the type of peers to synchronize</small>
|
||||||
|
</div>
|
||||||
|
<c-row class="g-2">
|
||||||
|
<c-col xs="12" md="6">
|
||||||
|
<label class="form-label-xs">Peers Type</label>
|
||||||
|
<select cSelect (change)="form_changed()" [(ngModel)]="SelectedCloner['pair_type']" class="form-select-xs">
|
||||||
|
<option value="devices">Devices</option>
|
||||||
|
<option value="groups" *ngIf="SelectedCloner['direction']=='oneway'">Groups</option>
|
||||||
|
</select>
|
||||||
|
</c-col>
|
||||||
|
</c-row>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<!-- Commands Configuration -->
|
||||||
|
<div class="cloner-form-section mb-3">
|
||||||
|
<div class="section-header mb-2">
|
||||||
|
<h6 class="section-title mb-0"><i class="fa-solid fa-terminal me-2"></i>Commands Configuration</h6>
|
||||||
|
</div>
|
||||||
|
<div class="commands-container-compact">
|
||||||
|
<div class="nav nav-underline commands-nav-compact">
|
||||||
|
<div class="nav-item" *ngFor="let tab of tabs; let i = index">
|
||||||
|
<a class="nav-link" [active]="i==0" [cTabContent]="tabContent" [tabPaneIdx]="i">{{ tab.name }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<c-tab-content class="command-sections-compact" #tabContent="cTabContent">
|
||||||
|
<c-tab-pane *ngFor="let tab of tabs; let i = index">
|
||||||
|
<div class="command-category-compact" *ngFor="let section of tab.sections">
|
||||||
|
<h6 class="category-title-compact">{{ section.title }}</h6>
|
||||||
|
<div class="commands-grid-compact">
|
||||||
|
<div class="command-item-compact" *ngFor="let command of section.commands">
|
||||||
|
<div class="command-content-compact">
|
||||||
|
<span class="command-name-compact">{{ command }}</span>
|
||||||
|
<div class="custom-switch-compact">
|
||||||
<input type="checkbox" class="custom-control-input" [checked]="in_active_commands(command)" (click)="activate_command(command)" [id]="command.replace('/', '')">
|
<input type="checkbox" class="custom-control-input" [checked]="in_active_commands(command)" (click)="activate_command(command)" [id]="command.replace('/', '')">
|
||||||
<label class="custom-control-label" [for]="command.replace('/', '')"></label>
|
<label class="custom-control-label" [for]="command.replace('/', '')"></label>
|
||||||
</div>
|
</div>
|
||||||
</c-card-body>
|
</div>
|
||||||
</c-card>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</c-tab-pane>
|
||||||
</c-tab-pane>
|
</c-tab-content>
|
||||||
</c-tab-content>
|
</div>
|
||||||
</c-col>
|
</div>
|
||||||
|
|
||||||
<h5>Peers :</h5>
|
<!-- Master Device Selection (for Master Mode) -->
|
||||||
<gui-grid [autoResizeWidth]="true" [source]="SelectedMembers" [columnMenu]="columnMenu" [sorting]="sorting"
|
<div class="cloner-form-section mb-2" *ngIf="SelectedCloner['direction']=='oneway' && SelectedMembers.length > 0">
|
||||||
[rowSelection]="rowSelection" [autoResizeWidth]=true [paging]="paging">
|
<div class="section-header mb-2">
|
||||||
<gui-grid-column header="Name" field="name">
|
<h6 class="section-title mb-0"><i class="fa-solid fa-crown me-2 text-warning"></i>Master Device</h6>
|
||||||
<ng-template let-value="item.name" let-item="item" let-index="index">
|
</div>
|
||||||
<i class="fa-solid fa-m" style="color: #ff3300;" *ngIf="SelectedCloner['direction']=='oneway' && item.id==master"></i>
|
<div class="master-selection-compact">
|
||||||
{{value}} </ng-template>
|
<div class="master-device-compact" *ngIf="master > 0">
|
||||||
</gui-grid-column>
|
<i class="fa-solid fa-server me-2 text-primary"></i>
|
||||||
<gui-grid-column *ngIf="SelectedCloner['pair_type']=='devices'" header="MAC" field="mac">
|
<strong>{{getMasterDeviceName()}}</strong>
|
||||||
<ng-template let-value="item.mac" let-item="item" let-index="index">
|
<c-badge color="warning" class="ms-2">Master</c-badge>
|
||||||
{{value}}
|
</div>
|
||||||
</ng-template>
|
<div class="no-master-compact" *ngIf="master == 0">
|
||||||
</gui-grid-column>
|
<i class="fa-solid fa-exclamation-triangle me-2 text-warning"></i>
|
||||||
<gui-grid-column header="Actions" width="120" field="action">
|
<span class="text-muted">No master device selected</span>
|
||||||
<ng-template let-value="item.id" let-item="item" let-index="index">
|
</div>
|
||||||
<button cButton color="danger" size="sm" [cTooltip]="'Delete Member'" (click)="remove_member(item)"><i
|
</div>
|
||||||
class="fa-regular fa-trash-can"></i></button>
|
</div>
|
||||||
<button *ngIf="SelectedCloner['direction']=='oneway'" cButton color="success" size="sm" style="margin-left: 5px;" [cTooltip]="'Set as Master'" (click)="set_master(item.id)"><i class="fa-regular fa-star"></i></button>
|
|
||||||
</ng-template>
|
<!-- Device Management -->
|
||||||
</gui-grid-column>
|
<div class="cloner-form-section">
|
||||||
</gui-grid>
|
<div class="section-header mb-2">
|
||||||
<hr />
|
<h6 class="section-title mb-0"><i class="fa-solid fa-server me-2"></i>Device Management</h6>
|
||||||
<button cButton color="primary" (click)="show_new_member_form()">+ Add new Members</button>
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<c-badge color="info">{{SelectedMembers.length}} device(s)</c-badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="peers-container">
|
||||||
|
<div *ngIf="SelectedMembers.length == 0" class="empty-peers">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="fa-solid fa-users-slash fa-2x text-muted opacity-50"></i>
|
||||||
|
</div>
|
||||||
|
<div class="empty-text">
|
||||||
|
<strong>No peers added</strong>
|
||||||
|
<p class="mb-0 text-muted">Click "Add Members" to start adding peers</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="SelectedMembers.length > 0">
|
||||||
|
<gui-grid [autoResizeWidth]="true" [source]="SelectedMembers" [columnMenu]="columnMenu" [sorting]="sorting"
|
||||||
|
[rowSelection]="rowSelection" [autoResizeWidth]=true [paging]="paging">
|
||||||
|
<gui-grid-column header="Name" field="name">
|
||||||
|
<ng-template let-value="item.name" let-item="item" let-index="index">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<i class="fa-solid fa-crown me-2 text-warning" *ngIf="SelectedCloner['direction']=='oneway' && item.id==master" title="Master Device"></i>
|
||||||
|
<i class="fa-solid fa-server me-2 text-primary" *ngIf="!(SelectedCloner['direction']=='oneway' && item.id==master)"></i>
|
||||||
|
<strong>{{value}}</strong>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</gui-grid-column>
|
||||||
|
<gui-grid-column *ngIf="SelectedCloner['pair_type']=='devices'" header="MAC" field="mac">
|
||||||
|
<ng-template let-value="item.mac" let-item="item" let-index="index">
|
||||||
|
<c-badge color="secondary">{{value}}</c-badge>
|
||||||
|
</ng-template>
|
||||||
|
</gui-grid-column>
|
||||||
|
<gui-grid-column header="Actions" width="120" field="action" align="center">
|
||||||
|
<ng-template let-value="item.id" let-item="item" let-index="index">
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button *ngIf="SelectedCloner['direction']=='oneway'" cButton color="warning" size="sm" variant="outline"
|
||||||
|
[cTooltip]="'Set as Master'" (click)="set_master(item.id)" [disabled]="item.id==master">
|
||||||
|
<i class="fa-solid fa-crown"></i>
|
||||||
|
</button>
|
||||||
|
<button cButton color="danger" size="sm" variant="outline"
|
||||||
|
[cTooltip]="'Remove Member'" (click)="remove_member(item)">
|
||||||
|
<i class="fa-solid fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</gui-grid-column>
|
||||||
|
</gui-grid>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="add-members-section mt-3">
|
||||||
|
<button cButton color="success" (click)="show_new_member_form()">
|
||||||
|
<i class="fa-solid fa-plus me-1"></i>Add Members
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</c-modal-body>
|
</c-modal-body>
|
||||||
<c-modal-footer>
|
<c-modal-footer>
|
||||||
<button *ngIf="SelectedCloner['action']=='add'" (click)="submit('add')" cButton color="primary">Add</button>
|
<button *ngIf="SelectedCloner['action']=='add'" (click)="submit('add')" cButton color="primary">Add</button>
|
||||||
|
|
@ -160,44 +251,75 @@
|
||||||
</c-modal>
|
</c-modal>
|
||||||
|
|
||||||
|
|
||||||
<c-modal #NewMemberModal backdrop="static" size="lg" [(visible)]="NewMemberModalVisible" id="NewMemberModal">
|
<c-modal #NewMemberModal backdrop="static" size="xl" [(visible)]="NewMemberModalVisible" id="NewMemberModal">
|
||||||
<c-modal-header>
|
<c-modal-header class="bg-success text-white">
|
||||||
<h5 cModalTitle>Editing Group </h5>
|
<h5 cModalTitle><i class="fa-solid fa-user-plus me-2"></i>Add Members to Cloner</h5>
|
||||||
<button (click)="NewMemberModalVisible=!NewMemberModalVisible" cButtonClose></button>
|
<button (click)="NewMemberModalVisible=!NewMemberModalVisible" cButtonClose></button>
|
||||||
</c-modal-header>
|
</c-modal-header>
|
||||||
<c-modal-body>
|
<c-modal-body class="p-4">
|
||||||
<c-input-group class="mb-3">
|
<!-- Selection Summary -->
|
||||||
<h5>Group Members :</h5>
|
<div class="mb-3" style="min-height: 58px;">
|
||||||
<gui-grid [autoResizeWidth]="true" *ngIf="NewMemberModalVisible" [searching]="searching"
|
<c-alert *ngIf="NewMemberRows.length > 0" color="info" class="d-flex align-items-center mb-0">
|
||||||
[source]="availbleMembers" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel"
|
<i class="fa-solid fa-info-circle me-2"></i>
|
||||||
[rowSelection]="rowSelection" (selectedRows)="onSelectedRowsNewMembers($event)" [autoResizeWidth]=true
|
<span><strong>{{NewMemberRows.length}}</strong> {{SelectedCloner['pair_type']}} selected for addition</span>
|
||||||
[paging]="paging">
|
</c-alert>
|
||||||
<gui-grid-column header="Member Name" field="name">
|
</div>
|
||||||
<ng-template let-value="item.name" let-item="item" let-index="index">
|
|
||||||
{{value}} </ng-template>
|
<!-- Available Members -->
|
||||||
</gui-grid-column>
|
<c-card>
|
||||||
<gui-grid-column *ngIf="SelectedCloner['pair_type']=='devices'" header="IP Address" field="ip">
|
<c-card-header class="bg-light">
|
||||||
<ng-template let-value="item.ip" let-item="item" let-index="index">
|
<h6 class="mb-0">
|
||||||
{{value}}
|
<i class="fa-solid me-2" [class.fa-server]="SelectedCloner['pair_type'] == 'devices'" [class.fa-layer-group]="SelectedCloner['pair_type'] != 'devices'"></i>
|
||||||
</ng-template>
|
Available {{SelectedCloner['pair_type'] | titlecase}} ({{availbleMembers.length}} total)
|
||||||
</gui-grid-column>
|
</h6>
|
||||||
<gui-grid-column *ngIf="SelectedCloner['pair_type']=='devices'" header="MAC Address" field="mac">
|
</c-card-header>
|
||||||
<ng-template let-value="item.mac" let-item="item" let-index="index">
|
<c-card-body class="p-0">
|
||||||
{{value}}
|
<div *ngIf="availbleMembers.length == 0" class="text-center p-4 text-muted">
|
||||||
</ng-template>
|
<i class="fa-solid fa-check-circle fa-3x mb-3 text-success opacity-50"></i>
|
||||||
</gui-grid-column>
|
<h6>All {{SelectedCloner['pair_type']}} are already added</h6>
|
||||||
</gui-grid>
|
<p class="mb-0">No available {{SelectedCloner['pair_type']}} to add to this cloner</p>
|
||||||
<br />
|
</div>
|
||||||
</c-input-group>
|
<div *ngIf="availbleMembers.length > 0">
|
||||||
<hr />
|
<gui-grid [autoResizeWidth]="true" *ngIf="NewMemberModalVisible" [searching]="searching"
|
||||||
|
[source]="availbleMembers" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel"
|
||||||
|
[rowSelection]="rowSelection" (selectedRows)="onSelectedRowsNewMembers($event)" [autoResizeWidth]=true
|
||||||
|
[paging]="paging">
|
||||||
|
<gui-grid-column header="Name" field="name">
|
||||||
|
<ng-template let-value="item.name" let-item="item" let-index="index">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<i class="fa-solid me-2 text-primary" [class.fa-server]="SelectedCloner['pair_type'] == 'devices'" [class.fa-layer-group]="SelectedCloner['pair_type'] != 'devices'"></i>
|
||||||
|
<strong>{{value}}</strong>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</gui-grid-column>
|
||||||
|
<gui-grid-column *ngIf="SelectedCloner['pair_type']=='devices'" header="IP Address" field="ip">
|
||||||
|
<ng-template let-value="item.ip" let-item="item" let-index="index">
|
||||||
|
<c-badge color="secondary">{{value}}</c-badge>
|
||||||
|
</ng-template>
|
||||||
|
</gui-grid-column>
|
||||||
|
<gui-grid-column *ngIf="SelectedCloner['pair_type']=='devices'" header="MAC Address" field="mac">
|
||||||
|
<ng-template let-value="item.mac" let-item="item" let-index="index">
|
||||||
|
<small class="text-muted">{{value}}</small>
|
||||||
|
</ng-template>
|
||||||
|
</gui-grid-column>
|
||||||
|
</gui-grid>
|
||||||
|
</div>
|
||||||
|
</c-card-body>
|
||||||
|
</c-card>
|
||||||
</c-modal-body>
|
</c-modal-body>
|
||||||
|
|
||||||
<c-modal-footer>
|
<c-modal-footer class="bg-light d-flex justify-content-between">
|
||||||
<button *ngIf="NewMemberRows.length!= 0" (click)="add_new_members()" cButton color="primary">Add {{
|
<div>
|
||||||
NewMemberRows.length }}</button>
|
<small class="text-muted">Select {{SelectedCloner['pair_type']}} from the list above to add them to the cloner</small>
|
||||||
<button (click)="NewMemberModalVisible=!NewMemberModalVisible" cButton color="secondary">
|
</div>
|
||||||
Close
|
<div>
|
||||||
</button>
|
<button *ngIf="NewMemberRows.length > 0" (click)="add_new_members()" cButton color="success">
|
||||||
|
<i class="fa-solid fa-plus me-1"></i>Add {{NewMemberRows.length}} {{SelectedCloner['pair_type'] | titlecase}}
|
||||||
|
</button>
|
||||||
|
<button (click)="NewMemberModalVisible=!NewMemberModalVisible" cButton color="secondary" class="ms-2">
|
||||||
|
<i class="fa-solid fa-times me-1"></i>Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</c-modal-footer>
|
</c-modal-footer>
|
||||||
</c-modal>
|
</c-modal>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -317,16 +317,27 @@ export class ClonerComponent implements OnInit {
|
||||||
this.data_provider
|
this.data_provider
|
||||||
.Add_cloner(_self.SelectedCloner, _self.SelectedClonerItems)
|
.Add_cloner(_self.SelectedCloner, _self.SelectedClonerItems)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
_self.initGridTable();
|
if (res.status === 'failed') {
|
||||||
|
_self.show_toast("Error", res.err, "danger");
|
||||||
|
} else {
|
||||||
|
_self.show_toast("Success", "Cloner added successfully", "success");
|
||||||
|
_self.initGridTable();
|
||||||
|
_self.EditClonerModalVisible = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.data_provider
|
this.data_provider
|
||||||
.Edit_cloner(_self.SelectedCloner, _self.SelectedClonerItems)
|
.Edit_cloner(_self.SelectedCloner, _self.SelectedClonerItems)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
_self.initGridTable();
|
if (res.status === 'failed') {
|
||||||
|
_self.show_toast("Error", res.err, "danger");
|
||||||
|
} else {
|
||||||
|
_self.show_toast("Success", "Cloner updated successfully", "success");
|
||||||
|
_self.initGridTable();
|
||||||
|
_self.EditClonerModalVisible = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.EditClonerModalVisible = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectedRowsNewMembers(rows: Array<GuiSelectedRow>): void {
|
onSelectedRowsNewMembers(rows: Array<GuiSelectedRow>): void {
|
||||||
|
|
@ -380,6 +391,7 @@ export class ClonerComponent implements OnInit {
|
||||||
members: "",
|
members: "",
|
||||||
action: "add",
|
action: "add",
|
||||||
};
|
};
|
||||||
|
this.master=0;
|
||||||
this.SelectedMembers = [];
|
this.SelectedMembers = [];
|
||||||
this.SelectedClonerItems = [];
|
this.SelectedClonerItems = [];
|
||||||
this.EditClonerModalVisible = true;
|
this.EditClonerModalVisible = true;
|
||||||
|
|
@ -411,6 +423,19 @@ export class ClonerComponent implements OnInit {
|
||||||
in_active_commands(command:string){
|
in_active_commands(command:string){
|
||||||
return this.active_commands.includes(command);
|
return this.active_commands.includes(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMasterDeviceName(): string {
|
||||||
|
const masterDevice = this.SelectedMembers.find((m: any) => m.id == this.master);
|
||||||
|
return masterDevice ? masterDevice.name : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
onDirectionChange() {
|
||||||
|
if (this.SelectedCloner['direction'] == 'twoway') {
|
||||||
|
this.SelectedCloner['live_mode'] = true;
|
||||||
|
this.SelectedCloner['schedule'] = false;
|
||||||
|
}
|
||||||
|
this.form_changed();
|
||||||
|
}
|
||||||
remove_member(item: any) {
|
remove_member(item: any) {
|
||||||
var _self = this;
|
var _self = this;
|
||||||
_self.SelectedMembers = _self.SelectedMembers.filter(
|
_self.SelectedMembers = _self.SelectedMembers.filter(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule, TitleCasePipe } from "@angular/common";
|
||||||
import { FormsModule,ReactiveFormsModule } from "@angular/forms";
|
import { FormsModule,ReactiveFormsModule } from "@angular/forms";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -13,6 +13,8 @@ import {
|
||||||
TooltipModule,
|
TooltipModule,
|
||||||
NavModule,
|
NavModule,
|
||||||
TabsModule,
|
TabsModule,
|
||||||
|
BadgeModule,
|
||||||
|
AlertModule,
|
||||||
} from "@coreui/angular";
|
} from "@coreui/angular";
|
||||||
import { ClonerRoutingModule } from "./cloner-routing.module";
|
import { ClonerRoutingModule } from "./cloner-routing.module";
|
||||||
import { ClonerComponent } from "./cloner.component";
|
import { ClonerComponent } from "./cloner.component";
|
||||||
|
|
@ -38,7 +40,10 @@ import { NgxSuperSelectModule} from "ngx-super-select";
|
||||||
TooltipModule,
|
TooltipModule,
|
||||||
NavModule,
|
NavModule,
|
||||||
TabsModule,
|
TabsModule,
|
||||||
|
BadgeModule,
|
||||||
|
AlertModule,
|
||||||
],
|
],
|
||||||
declarations: [ClonerComponent],
|
declarations: [ClonerComponent],
|
||||||
|
providers: [TitleCasePipe],
|
||||||
})
|
})
|
||||||
export class ClonerModule {}
|
export class ClonerModule {}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
@import 'cloner-styles';
|
||||||
|
|
||||||
.nav-underline {
|
.nav-underline {
|
||||||
border-bottom: 2px solid var(--cui-nav-underline-border-color, #c4c9d0)
|
border-bottom: 2px solid var(--cui-nav-underline-border-color, #c4c9d0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue