feat: redesign password vault interface for pro users

- Complete UI/UX redesign of vault module
- Enhanced security interface
- Pro feature improvements
- Better password management workflow
This commit is contained in:
sepehr 2025-10-16 17:34:05 +03:00
parent cdc2e0cabf
commit 746711fa24
4 changed files with 632 additions and 335 deletions

View file

@ -1,58 +1,69 @@
<c-row>
<c-col xs style="padding-right: 0;">
<div class="nav nav-underline" style="background: #fff;">
<div calss="nav-item">
<a [active]="true" class="nav-link" [cTabContent]="tabContent" (click)="activetab=0" [tabPaneIdx]="0">Settings</a>
<c-card class="mb-4" style="margin-bottom: 0 !important;">
<c-card-header style="padding: 0;">
<c-row class="align-items-center">
<c-col xs="12" md="6">
<div class="nav nav-underline bg-transparent">
<div class="nav-item">
<a [active]="activetab==0" class="nav-link" [cTabContent]="tabContent" (click)="activetab=0" [tabPaneIdx]="0">
<i class="fas fa-cog me-1"></i>Settings
</a>
</div>
<div calss="nav-item">
<a [cTabContent]="tabContent" (click)="get_passwords();activetab=1" class="nav-link" [routerLink] [tabPaneIdx]="1">Passwords</a>
<div class="nav-item">
<a [active]="activetab==1" class="nav-link" [cTabContent]="tabContent" (click)="get_passwords();activetab=1" [tabPaneIdx]="1">
<i class="fas fa-key me-1"></i>Passwords
</a>
</div>
</div>
</c-col>
<c-col style="padding-left: 0;">
<div class="nav nav-underline" style="background: #fff;padding: 3px;flex-direction: row-reverse;">
<button *ngIf="activetab==0" cButton size="sm" shape="rounded-0" class="mx-2" (click)="runConfirmModalVisible=!runConfirmModalVisible" color="danger">Execute Now</button>
<button *ngIf="activetab==1" cButton size="sm" shape="rounded-0" class="mx-2" (click)="toggleCollapse()" color="info">filters</button>
</div>
<c-col xs="12" md="6" class="text-end mt-2 mt-md-0">
<button *ngIf="activetab==0" cButton size="sm" class="me-2" (click)="runConfirmModalVisible=!runConfirmModalVisible" color="danger">
<i class="fas fa-play me-1"></i>Execute Now
</button>
<button *ngIf="activetab==1" cButton size="sm" (click)="toggleCollapse()" color="info">
<i class="fas fa-filter me-1"></i>Filters
</button>
</c-col>
</c-row>
<c-tab-content style="padding: 0!important;" #tabContent="cTabContent">
</c-row>
</c-card-header>
</c-card>
<c-tab-content #tabContent="cTabContent">
<c-tab-pane>
<c-row>
<c-col xs>
<c-card class="mb-4" style="border-radius: 0;" *ngIf="settings">
<c-card class="mb-4" *ngIf="settings" style="border: none;">
<c-card-body>
<c-row>
<c-col md="6">
<c-input-group class="mb-3">
<label cInputGroupText for="inputGroupSelect01">
Status
</label>
<select cSelect id="inputGroupSelect01" [(ngModel)]="settings['enable']">
<!-- Settings Tab Header -->
<div class="tab-info-header mb-4">
<div class="d-flex align-items-center">
<i class="fas fa-cog text-primary me-2 fs-4"></i>
<div>
<h5 class="mb-1">Password Vault Settings</h5>
<p class="text-muted mb-0 small">Configure automated password management for your device groups. Set schedules, define password policies, and manage affected devices.</p>
</div>
</div>
</div>
<!-- Password Vault Configuration -->
<div class="mb-4 pb-4 border-bottom">
<h6 class="mb-3"><i class="fas fa-shield-alt me-2"></i>Password Vault Configuration</h6>
<c-row class="g-3">
<c-col xs="12" md="6" lg="3">
<label class="form-label small">Status</label>
<select cSelect size="sm" [(ngModel)]="settings['enable']" class="form-select-sm">
<option>Choose...</option>
<option value="enable">Enable</option>
<option value="disable">Disable</option>
</select>
</c-input-group>
</c-col>
<c-col md="6">
<c-input-group class="mb-3">
<label cInputGroupText for="inputGroupSelect01">
Strategy
</label>
<select cSelect id="inputGroupSelect01" [(ngModel)]="settings['strategy']">
<c-col xs="12" md="6" lg="3">
<label class="form-label small">Strategy <i class="fas fa-info-circle text-info ms-1" [cTooltip]="strategyTooltipTemplate" cTooltipPlacement="top"></i></label>
<select cSelect size="sm" [(ngModel)]="settings['strategy']" class="form-select-sm">
<option>Choose...</option>
<option value="all">All local</option>
<option value="mikrowizard">Defined in MikroWizard</option>
<option value="all">All Local Users</option>
<option value="mikrowizard">MikroWizard Users Only</option>
</select>
</c-input-group>
</c-col>
<c-col md="6">
<c-input-group class="mb-3">
<label cInputGroupText for="inputGroupSelect01">
Interval
</label>
<select cSelect id="inputGroupSelect01" [(ngModel)]="settings['interval']">
<c-col xs="12" md="6" lg="3">
<label class="form-label small">Interval <i class="fas fa-info-circle text-info ms-1" [cTooltip]="intervalTooltipTemplate" cTooltipPlacement="top"></i></label>
<select cSelect size="sm" [(ngModel)]="settings['interval']" class="form-select-sm">
<option>Choose...</option>
<option value="daily">Daily</option>
<option value="weekly">Weekly</option>
@ -61,276 +72,286 @@
<option value="manual">Manual</option>
<option value="custom">Custom</option>
</select>
</c-input-group>
<c-input-group *ngIf="settings['interval']=='custom'" class="mb-3">
<label cInputGroupText for="inputGroupSelect01">
Custom Cron
</label>
<input cFormControl id="floatingInput" style="border-radius: 0;" placeholder="Cron" [(ngModel)]="settings['cron']" />
</c-input-group>
</c-col>
<c-col md="6">
<c-input-group class="mb-3">
<label cInputGroupText for="inputGroupSelect01">
Password
</label>
<select cSelect id="inputGroupSelect01" [(ngModel)]="settings['password_type']">
<c-col xs="12" md="6" lg="3">
<label class="form-label small">Password Type <i class="fas fa-info-circle text-info ms-1" [cTooltip]="passwordTypeTooltipTemplate" cTooltipPlacement="top"></i></label>
<select cSelect size="sm" [(ngModel)]="settings['password_type']" class="form-select-sm">
<option>Choose...</option>
<option value="random">Random</option>
<option value="defined">Pre-defined</option>
</select>
</c-input-group>
</c-col>
<c-col md="12" *ngIf="settings['strategy']=='all'">
<hr width="70%" style="margin: 10px auto;border-color: #304193;border-width: 2px;" />
<c-row class="gui-header" style="background: #f9fafb;padding: 10px 0px;margin: 0 auto;height: unset;border: 1px solid #e8e8e8;border-bottom: unset;">
<c-col md="2" style="display: flex;align-items: center;">
<h6>User Exceptions</h6>
</c-col>
<c-col style="display: flex;flex-direction: row-reverse;" md="10">
<table>
<td>
<div>
<input cFormControl style="border-radius: 0;" id="floatingInput" placeholder="Username Exception"
[(ngModel)]="new_exception" />
</div>
</td>
<td style="vertical-align: top;">
<button cButton color="dark" shape="rounded-0" (click)="add_exception()">Add Username</button>
</td>
</table>
<c-col xs="12" *ngIf="settings['interval']=='custom'">
<label class="form-label small">Custom Cron Expression</label>
<input cFormControl size="sm" placeholder="0 0 * * *" [(ngModel)]="settings['cron']" />
</c-col>
</c-row>
<c-input-group class="mb-3">
<gui-grid [autoResizeWidth]="true" [source]="settings['exceptions']" [columnMenu]="columnMenu" [paging]="paging"
[sorting]="sorting" [autoResizeWidth]=true>
<gui-grid-column header="UserName" field="name">
</div>
<!-- User Exceptions -->
<div class="mb-4 pb-4 border-bottom" *ngIf="settings && settings['strategy']=='all'">
<c-row class="align-items-center mb-3">
<c-col>
<h6 class="mb-0"><i class="fas fa-user-shield me-2"></i>User Exceptions</h6>
<small class="text-muted">Users excluded from password changes</small>
</c-col>
<c-col xs="auto">
<div class="input-group input-group-sm">
<input cFormControl size="sm" placeholder="Username" [(ngModel)]="new_exception" />
<button cButton color="primary" size="sm" (click)="add_exception()">
<i class="fas fa-plus"></i>
</button>
</div>
</c-col>
</c-row>
<div *ngIf="settings['exceptions']?.length > 0">
<gui-grid [autoResizeWidth]="true" [source]="settings['exceptions']" [columnMenu]="columnMenu" [paging]="paging" [sorting]="sorting">
<gui-grid-column header="Username" field="name">
<ng-template let-value="item" let-item="item" let-index="index">
&nbsp; {{value}} </ng-template>
<i class="fas fa-user me-2 text-muted"></i>{{value}}
</ng-template>
</gui-grid-column>
<gui-grid-column header="Actions" width="70" field="action">
<gui-grid-column header="Actions" width="80" field="action" align="center">
<ng-template let-value="item.id" let-item="item" let-index="index">
<button (click)="remove_exception(item)" class=" mx-1" cButton color="danger" size="sm"><i
class="fa-regular fa-trash-can"></i></button>
<button (click)="remove_exception(item)" cButton color="danger" size="sm" variant="outline">
<i class="fas fa-trash"></i>
</button>
</ng-template>
</gui-grid-column>
</gui-grid>
</c-input-group>
</c-col>
<c-col md="12" *ngIf="settings['password_type']=='defined'">
<hr width="70%" style="margin: 10px auto;border-color: #304193;border-width: 2px;"/>
<c-row class="gui-header" style="background: #f9fafb;padding: 10px 0px;margin: 0 auto;height: unset;border: 1px solid #e8e8e8;border-bottom: unset;">
<c-col md="2" style="display: flex;align-items: center;">
<h6>Password list</h6>
</c-col>
<c-col style="display: flex;flex-direction: row-reverse;" md="10">
<table>
<td>
<div>
<input cFormControl id="floatingInput" style="border-radius: 0;" placeholder="Password" [(ngModel)]="new_password" />
</div>
</td>
<td style="vertical-align: top;">
<button cButton color="dark" shape="rounded-0" (click)="add_password()">Add Password</button>
</td>
</table>
</div>
<!-- Pre-defined Passwords -->
<div class="mb-4 pb-4 border-bottom" *ngIf="settings && settings['password_type']=='defined'">
<c-row class="align-items-center mb-3">
<c-col>
<h6 class="mb-0"><i class="fas fa-key me-2"></i>Password List</h6>
<small class="text-muted">Pre-defined passwords for rotation</small>
</c-col>
<c-col xs="auto">
<div class="input-group input-group-sm">
<input cFormControl size="sm" type="password" placeholder="Password" [(ngModel)]="new_password" />
<button cButton color="primary" size="sm" (click)="add_password()">
<i class="fas fa-plus"></i>
</button>
</div>
</c-col>
</c-row>
<gui-grid [autoResizeWidth]="true" [source]="settings['passwords']" [columnMenu]="columnMenu" [sorting]="sorting"
[paging]="paging" [autoResizeWidth]=true>
<div *ngIf="settings['passwords']?.length > 0">
<gui-grid [autoResizeWidth]="true" [source]="settings['passwords']" [columnMenu]="columnMenu" [sorting]="sorting" [paging]="paging">
<gui-grid-column header="Password" field="name">
<ng-template let-value="item" let-item="item" let-index="index">
&nbsp; {{value}} </ng-template>
<i class="fas fa-lock me-2 text-muted"></i>••••••••
</ng-template>
</gui-grid-column>
<gui-grid-column header="Actions" width="70" field="action">
<gui-grid-column header="Actions" width="80" field="action" align="center">
<ng-template let-value="item.id" let-item="item" let-index="index">
<button class=" mx-1" cButton color="danger" size="sm"><i
class="fa-regular fa-trash-can"></i></button>
<button cButton color="danger" size="sm" variant="outline" (click)="remove_password(item)">
<i class="fas fa-trash"></i>
</button>
</ng-template>
</gui-grid-column>
</gui-grid>
</div>
</div>
<!-- Affected Groups -->
<div class="mb-4 pb-4 border-bottom">
<c-row class="align-items-center mb-3">
<c-col>
<h6 class="mb-0"><i class="fas fa-network-wired me-2"></i>Affected Device Groups</h6>
<small class="text-muted">Device groups where password changes will be applied</small>
</c-col>
<c-col xs="auto">
<button cButton color="primary" size="sm" (click)="show_new_member_form()">
<i class="fas fa-plus me-1"></i>Add Groups
</button>
</c-col>
</c-row>
</c-card-body>
<c-card-footer style="display: flex;flex-direction: row-reverse;">
<button cButton color="info" shape="rounded-0" (click)="save_settings()" style="color: #fff;">Save Settings</button>
</c-card-footer>
</c-card>
</c-col>
</c-row>
<c-row>
<c-col xs>
<c-card class="mb-4" style="border-radius: 0;" *ngIf="settings">
<c-card-body>
<h6>Efected Groups</h6>
<gui-grid [autoResizeWidth]="true" [source]="Members" [columnMenu]="columnMenu" [sorting]="sorting"
[paging]="paging" [autoResizeWidth]=true>
<gui-grid-column header="Name" field="name">
<div *ngIf="Members?.length > 0" class="mb-3">
<gui-grid [autoResizeWidth]="true" [source]="Members" [columnMenu]="columnMenu" [sorting]="sorting" [paging]="paging">
<gui-grid-column header="Group Name" field="name">
<ng-template let-value="item.name" let-item="item" let-index="index">
&nbsp; {{value}} </ng-template>
<i class="fas fa-server me-2 text-primary"></i>{{value}}
</ng-template>
</gui-grid-column>
<gui-grid-column header="Actions" width="70" field="action">
<gui-grid-column header="Actions" width="80" field="action" align="center">
<ng-template let-value="item.id" let-item="item" let-index="index">
<button (click)="delete_group(item.id)" class=" mx-1" cButton color="danger" size="sm"><i
class="fa-regular fa-trash-can"></i></button>
<button (click)="delete_group(item.id)" cButton color="danger" size="sm" variant="outline">
<i class="fas fa-trash"></i>
</button>
</ng-template>
</gui-grid-column>
</gui-grid>
</c-card-body>
<c-card-footer style="display: flex;flex-direction: row-reverse;">
<button cButton color="info" shape="rounded-0" (click)="save_settings()" style="color: #fff;">Save Settings</button>
<button cButton color="primary" class="mx-1" (click)="show_new_member_form()">+ Add new Members</button>
</c-card-footer>
</c-card>
</c-col>
</c-row>
<c-row>
<c-col xs>
<c-card class="mb-4" style="border-radius: 0;" *ngIf="settings">
<c-card-body *ngIf="vault_history">
<h6>Reports</h6>
<gui-grid [autoResizeWidth]="true" [source]="vault_history" [columnMenu]="columnMenu" [sorting]="sorting"
[paging]="paging" [autoResizeWidth]=true>
<gui-grid-column header="Start Time" field="name">
</div>
<div class="text-end">
<button cButton color="success" (click)="save_settings()">
<i class="fas fa-save me-1"></i>Save Configuration
</button>
</div>
</div>
<!-- Execution History -->
<div *ngIf="vault_history">
<h6 class="mb-3"><i class="fas fa-history me-2"></i>Execution Reports</h6>
<gui-grid [autoResizeWidth]="true" [source]="vault_history" [columnMenu]="columnMenu" [sorting]="sorting" [paging]="paging">
<gui-grid-column header="Start Time" field="started">
<ng-template let-value="item.started" let-item="item" let-index="index">
&nbsp; {{value}} </ng-template>
<small>{{value}}</small>
</ng-template>
</gui-grid-column>
<gui-grid-column header="End Time" field="name">
<gui-grid-column header="End Time" field="ended">
<ng-template let-value="item.ended" let-item="item" let-index="index">
&nbsp; {{value}} </ng-template>
<small>{{value}}</small>
</ng-template>
</gui-grid-column>
<gui-grid-column header="Logs" field="mac" align="center">
<gui-grid-column header="Results" field="result" align="center" width="100">
<ng-template let-value="item['result']" let-item="item" let-index="index">
<button (click)="exportToCsv(value)" color="primary" cButton>download</button>
<button (click)="exportToCsv(value)" color="primary" size="sm" cButton variant="outline">
<i class="fas fa-download"></i>
</button>
</ng-template>
</gui-grid-column>
</gui-grid>
</div>
</c-card-body>
</c-card>
</c-col>
</c-row>
</c-tab-pane>
<c-tab-pane>
<c-row>
<div [visible]="filters_visible" cCollapse>
<c-col xs [lg]="12" class="example-form" style="background: #fff;padding: 0 10px;">
<mat-form-field *ngIf="ispro">
<c-card class="mb-4">
<c-card-body>
<!-- Passwords Tab Header -->
<div class="tab-info-header mb-4">
<div class="d-flex align-items-center">
<i class="fas fa-key text-warning me-2 fs-4"></i>
<div>
<h5 class="mb-1">Stored Passwords</h5>
<p class="text-muted mb-0 small">View and manage passwords stored by the vault system. Use filters to find specific devices or users.</p>
</div>
</div>
</div>
<!-- Filters -->
<div class="mb-4 pb-4 border-bottom" [visible]="filters_visible" cCollapse>
<h6 class="mb-3"><i class="fas fa-filter me-2"></i>Filters</h6>
<c-row class="g-3">
<c-col xs="12" md="4" *ngIf="ispro">
<mat-form-field appearance="outline" class="w-100">
<mat-label>Username</mat-label>
<input (ngModelChange)="reinitgrid('username',$event)" [(ngModel)]="filters['username']" matInput>
</mat-form-field>
<mat-form-field *ngIf="ispro">
</c-col>
<c-col xs="12" md="4" *ngIf="ispro">
<mat-form-field appearance="outline" class="w-100">
<mat-label>Device IP</mat-label>
<input (ngModelChange)="reinitgrid('dev_ip',$event)" [(ngModel)]="filters['dev_ip']" matInput>
</mat-form-field>
<mat-form-field *ngIf="ispro">
</c-col>
<c-col xs="12" md="4" *ngIf="ispro">
<mat-form-field appearance="outline" class="w-100">
<mat-label>Device Name</mat-label>
<input (ngModelChange)="reinitgrid('dev_name',$event)" [(ngModel)]="filters['dev_name']" matInput>
</mat-form-field>
</c-col>
</div>
</c-row>
<c-row>
<c-col xs>
<c-card class="mb-4">
<c-card-body *ngIf="passwords">
<gui-grid [autoResizeWidth]="true" [source]="passwords" [columnMenu]="columnMenu" [sorting]="sorting"
[infoPanel]="infoPanel" [autoResizeWidth]=true>
<gui-grid-column header="Device Name" field="name">
</div>
<!-- Stored Passwords -->
<div *ngIf="passwords">
<h6 class="mb-3"><i class="fas fa-database me-2"></i>Stored Passwords</h6>
<gui-grid [autoResizeWidth]="true" [source]="passwords" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel">
<gui-grid-column header="Device" field="name">
<ng-template let-value="item.name" let-item="item" let-index="index">
&nbsp; {{value}} </ng-template>
</gui-grid-column>
<gui-grid-column header="Device IP" field="devip">
<ng-template let-value="item.devip" let-item="item" let-index="index">
{{value}}
<div class="d-flex align-items-center">
<i class="fas fa-router me-2 text-primary"></i>
<div>
<div class="fw-semibold">{{value}}</div>
<small class="text-muted">{{item.devip}}</small>
</div>
</div>
</ng-template>
</gui-grid-column>
<gui-grid-column header="UserName" field="username">
<gui-grid-column header="Username" field="username">
<ng-template let-value="item.username" let-item="item" let-index="index">
{{value}}
<i class="fas fa-user me-2 text-muted"></i>{{value}}
</ng-template>
</gui-grid-column>
<gui-grid-column header="Last Changed" field="desc_cron">
<gui-grid-column header="Last Changed" field="changed">
<ng-template let-value="item.changed" let-item="item" let-index="index">
{{value}}
<small>{{value}}</small>
</ng-template>
</gui-grid-column>
<gui-grid-column header="Actions" width="120" field="action">
<gui-grid-column header="Actions" width="100" field="action" align="center">
<ng-template let-value="item.id" let-item="item" let-index="index">
<button cButton *ngIf="ispro" (click)="reveal_password(item.devid,item.username)" color="info" variant="outline">
<i class="fa-solid fa-eye"></i>
<button cButton *ngIf="ispro" (click)="reveal_password(item.devid,item.username)" color="info" size="sm" variant="outline">
<i class="fas fa-eye"></i>
</button>
</ng-template>
</gui-grid-column>
</gui-grid>
</div>
</c-card-body>
</c-card>
</c-col>
</c-row>
</c-tab-pane>
</c-tab-content>
<!-- Password Reveal Modal -->
<c-modal #PasswordModal backdrop="static" [(visible)]="PasswordModalVisible" id="PasswordModal">
<c-modal-header>
<h6 cModalTitle>Password</h6>
<h6 cModalTitle><i class="fas fa-eye me-2"></i>Password Reveal</h6>
<button [cModalToggle]="PasswordModal.id" cButtonClose></button>
</c-modal-header>
<c-modal-body>
<p>
<c-input-group class="mb-3">
<label cInputGroupText for="inputGroupSelect01">
Password
</label>
<input [value]="password" cFormControl disabled="true"/>
</c-input-group>
</p>
<code>
Your attempt to reveal password is logged in system!
</code>
<div class="alert alert-warning">
<i class="fas fa-exclamation-triangle me-2"></i>
<strong>Security Notice:</strong> Your attempt to reveal this password is logged in the system.
</div>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-key"></i></span>
<input [value]="password" cFormControl disabled="true" class="form-control"/>
<button cButton color="secondary" variant="outline" (click)="copyToClipboard(password)">
<i class="fas fa-copy"></i>
</button>
</div>
</c-modal-body>
<c-modal-footer>
<button [cModalToggle]="PasswordModal.id" cButton color="info">
Close
</button>
<button [cModalToggle]="PasswordModal.id" cButton color="secondary">Close</button>
</c-modal-footer>
</c-modal>
<!-- Execute Confirmation Modal -->
<c-modal #runConfirmModal backdrop="static" [(visible)]="runConfirmModalVisible" id="runConfirmModal">
<c-modal-header>
<h6 cModalTitle>Confirm RUN {{ SelectedTask['name'] }}</h6>
<h6 cModalTitle><i class="fas fa-exclamation-triangle me-2 text-warning"></i>Confirm Execution</h6>
<button [cModalToggle]="runConfirmModal.id" cButtonClose></button>
</c-modal-header>
<c-modal-body>
Are you sure that You want to run Vault Password Job ?
<br />
<div class="alert alert-warning">
<strong>Warning:</strong> This will execute the password vault job and change passwords on all configured device groups.
</div>
<p>Are you sure you want to proceed with the password vault execution?</p>
</c-modal-body>
<c-modal-footer>
<button cButton color="danger" (click)="exec_vault()">
Yes,Run!
</button>
<button [cModalToggle]="runConfirmModal.id" cButton color="info">
Close
<i class="fas fa-play me-1"></i>Yes, Execute
</button>
<button [cModalToggle]="runConfirmModal.id" cButton color="secondary">Cancel</button>
</c-modal-footer>
</c-modal>
<!-- Add Device Groups Modal -->
<c-modal #NewMemberModal backdrop="static" size="lg" [(visible)]="NewMemberModalVisible" id="NewMemberModal">
<c-modal-header>
<h5 cModalTitle>Editing Group </h5>
<h6 cModalTitle><i class="fas fa-plus me-2"></i>Add Device Groups</h6>
<button (click)="NewMemberModalVisible=!NewMemberModalVisible" cButtonClose></button>
</c-modal-header>
<c-modal-body>
<c-input-group class="mb-3">
<h5>Group Members :</h5>
<p class="text-muted mb-3">Select device groups to include in the password vault task:</p>
<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="Member Name" field="name">
[rowSelection]="rowSelection" (selectedRows)="onSelectedRowsNewMembers($event)" [paging]="paging">
<gui-grid-column header="Group Name" field="name">
<ng-template let-value="item.name" let-item="item" let-index="index">
&nbsp; {{value}} </ng-template>
<i class="fas fa-server me-2 text-primary"></i>{{value}}
</ng-template>
</gui-grid-column>
<gui-grid-column *ngIf="SelectedTask['selection_type']=='devices'" header="IP Address" field="ip">
<ng-template let-value="item.ip" let-item="item" let-index="index">
@ -343,18 +364,35 @@
</ng-template>
</gui-grid-column>
</gui-grid>
<br />
</c-input-group>
<hr />
</c-modal-body>
<c-modal-footer>
<button *ngIf="NewMemberRows.length!= 0" (click)="add_new_members()" cButton color="primary">Add {{
NewMemberRows.length }}</button>
<button (click)="NewMemberModalVisible=!NewMemberModalVisible" cButton color="secondary">
Close
<button *ngIf="NewMemberRows.length != 0" (click)="add_new_members()" cButton color="primary">
<i class="fas fa-plus me-1"></i>Add {{NewMemberRows.length}} Group(s)
</button>
<button (click)="NewMemberModalVisible=!NewMemberModalVisible" cButton color="secondary">Cancel</button>
</c-modal-footer>
</c-modal>
<!-- Tooltip Templates -->
<ng-template #strategyTooltipTemplate>
<strong>All Local Users:</strong><br>
Changes passwords for all router users except exceptions.<br><br>
<strong>MikroWizard Users Only:</strong><br>
Changes only passwords for users defined in MikroWizard system.
</ng-template>
<ng-template #intervalTooltipTemplate>
<strong>Frequency:</strong><br>
How often passwords should be changed automatically.<br><br>
<strong>Manual:</strong><br>
Only when executed manually.
</ng-template>
<ng-template #passwordTypeTooltipTemplate>
<strong>Random:</strong><br>
System generates random passwords.<br><br>
<strong>Pre-defined:</strong><br>
Use passwords from your custom list.
</ng-template>
<c-toaster position="fixed" placement="top-end"></c-toaster>

View file

@ -435,6 +435,14 @@ export class VaultComponent implements OnInit {
copyToClipboard(text: string) {
navigator.clipboard.writeText(text).then(() => {
this.show_toast('Success', 'Password copied to clipboard', 'success');
}).catch(() => {
this.show_toast('Error', 'Failed to copy password', 'danger');
});
}
logger(item: any) {
console.dir(item);
}

View file

@ -12,6 +12,7 @@ import {
TabsModule,
ToastModule,
CollapseModule,
TooltipModule,
} from "@coreui/angular";
import { VaultRoutingModule } from "./vault-routing.module";
import { VaultComponent } from "./vault.component";
@ -36,7 +37,8 @@ import { MatFormFieldModule } from "@angular/material/form-field";
ToastModule,
MatInputModule,
MatFormFieldModule,
CollapseModule
CollapseModule,
TooltipModule
],
declarations: [VaultComponent],
})

View file

@ -1,29 +1,278 @@
// Compact Navigation Styles
.nav-underline {
border-bottom: 2px solid var(--cui-nav-underline-border-color, #c4c9d0)
}
border-bottom: 2px solid var(--cui-nav-underline-border-color, #c4c9d0);
.nav-underline .nav-item {
.nav-item {
margin-bottom: -2px;
cursor: pointer;
}
}
.nav-underline .nav-link {
.nav-link {
color: var(--cui-nav-underline-link-color, #768192);
border-style: none none solid!important;
border-width:2px;
position:relative;
bottom:-1px;
border-width: 2px;
position: relative;
bottom: -1px;
cursor: pointer;
font-size: 0.9rem;
padding: 0.5rem 1rem;
}
&:hover, &:focus {
border-color: var(--cui-nav-underline-link-active-border-color, #321fdb);
}
.nav-underline .nav-link:hover,.nav-underline .nav-link:focus {
border-color: var(--cui-nav-underline-link-active-border-color, #321fdb)
}
.nav-underline .nav-link.active,.nav-underline .show>.nav-link {
&.active, .show > & {
color: var(--cui-nav-underline-link-active-color, #321fdb);
background: transparent;
border-color: var(--cui-nav-underline-link-active-border-color, #321fdb)
border-color: var(--cui-nav-underline-link-active-border-color, #321fdb);
}
}
}
// Compact Form Styles
.form-label.small {
font-size: 0.8rem;
font-weight: 600;
color: #495057;
margin-bottom: 0.25rem;
}
.form-select-sm {
font-size: 0.85rem;
padding: 0.25rem 0.5rem;
}
// Card Header Improvements
.c-card-header {
background: #f8f9fa;
border-bottom: 1px solid #dee2e6;
padding: 0.75rem 1rem;
h6 {
font-size: 0.9rem;
font-weight: 600;
color: #495057;
i {
color: #6c757d;
}
}
.text-muted {
font-size: 0.75rem;
}
}
// Grid Improvements
gui-grid {
.gui-grid {
font-size: 0.85rem;
}
}
// Input Group Compact
.input-group-sm {
.form-control, .input-group-text {
font-size: 0.8rem;
padding: 0.25rem 0.5rem;
}
}
// Alert Improvements
.alert {
font-size: 0.85rem;
padding: 0.5rem 0.75rem;
i {
margin-right: 0.5rem;
}
}
// Button Improvements
.btn {
font-size: 0.85rem;
&.btn-sm {
font-size: 0.8rem;
padding: 0.25rem 0.5rem;
}
i {
font-size: 0.8rem;
}
}
// Modal Improvements
.c-modal-header {
padding: 0.75rem 1rem;
h6 {
font-size: 0.95rem;
font-weight: 600;
}
}
.c-modal-body {
padding: 1rem;
font-size: 0.9rem;
}
.c-modal-footer {
padding: 0.75rem 1rem;
}
// Responsive Design
@media (max-width: 768px) {
.c-card-header {
.nav-underline {
margin-bottom: 0.5rem;
}
.text-end {
text-align: left !important;
}
}
.input-group-sm {
flex-direction: column;
.form-control {
margin-bottom: 0.25rem;
}
.btn {
width: 100%;
}
}
.c-modal-dialog {
margin: 0.5rem;
}
.c-card-footer {
.btn {
width: 100%;
margin-bottom: 0.5rem;
&:last-child {
margin-bottom: 0;
}
}
}
}
@media (max-width: 576px) {
.nav-underline .nav-link {
font-size: 0.8rem;
padding: 0.4rem 0.8rem;
}
.c-card-header h6 {
font-size: 0.85rem;
}
.form-label.small {
font-size: 0.75rem;
}
.btn {
font-size: 0.8rem;
&.btn-sm {
font-size: 0.75rem;
padding: 0.2rem 0.4rem;
}
}
}
// Material Form Field Compact
::ng-deep .mat-form-field {
.mat-form-field-wrapper {
padding-bottom: 0.5em;
}
.mat-form-field-infix {
padding: 0.5em 0;
}
.mat-form-field-label {
font-size: 0.85rem;
}
input {
font-size: 0.85rem;
}
}
// Grid Cell Improvements
::ng-deep gui-grid {
.gui-grid-cell {
padding: 0.5rem;
font-size: 0.85rem;
}
.gui-grid-header-cell {
font-size: 0.8rem;
font-weight: 600;
padding: 0.5rem;
}
}
// Tab Info Headers
.tab-info-header {
padding: 1rem 0;
h5 {
color: #495057;
font-weight: 600;
font-size: 1.1rem;
}
.fs-4 {
font-size: 1.5rem !important;
}
}
// Utility Classes
.fw-semibold {
font-weight: 600;
}
.text-xs {
font-size: 0.75rem;
}
.compact-spacing {
.c-row {
margin-bottom: 0.5rem;
}
.c-col {
padding: 0.25rem;
}
}
// Responsive tab headers
@media (max-width: 576px) {
.tab-info-header {
padding: 0.75rem 0;
h5 {
font-size: 1rem;
}
.fs-4 {
font-size: 1.25rem !important;
}
.d-flex {
flex-direction: column;
text-align: center;
}
.me-2 {
margin-right: 0 !important;
margin-bottom: 0.5rem;
}
}
}