mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 10:15:35 +01:00
Merge pull request #2136 from wknet123/master-ui-aot
Fix issue about UX.
This commit is contained in:
commit
fc47d3932c
@ -97,7 +97,7 @@ script:
|
||||
- docker-compose -f make/docker-compose.test.yml down
|
||||
- sudo rm -rf /data/config/*
|
||||
- ls /data/cert
|
||||
- sudo make install GOBUILDIMAGE=golang:1.7.3 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.1.0 NOTARYFLAG=true
|
||||
- sudo make install GOBUILDIMAGE=golang:1.7.3 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.1.1 NOTARYFLAG=true
|
||||
|
||||
- docker ps
|
||||
- ./tests/notarytest.sh
|
||||
|
2
Makefile
2
Makefile
@ -91,7 +91,7 @@ NEWCLARITYVERSION=
|
||||
|
||||
#clarity parameters
|
||||
CLARITYIMAGE=vmware/harbor-clarity-ui-builder[:tag]
|
||||
CLARITYSEEDPATH=/clarity-seed
|
||||
CLARITYSEEDPATH=/harbor_ui
|
||||
CLARITYBUILDSCRIPT=/entrypoint.sh
|
||||
|
||||
# docker parameters
|
||||
|
@ -1,20 +1,20 @@
|
||||
FROM node:7.5.0
|
||||
|
||||
RUN mkdir -p /clarity-seed
|
||||
RUN mkdir -p /harbor_ui
|
||||
|
||||
COPY src/ui_ng/package.json /clarity-seed
|
||||
COPY src/ui_ng/tslint.json /clarity-seed
|
||||
COPY src/ui_ng/typings.json /clarity-seed
|
||||
COPY src/ui_ng/yarn.lock /clarity-seed
|
||||
COPY make/dev/nodeclarity/angular-cli.json /clarity-seed
|
||||
COPY src/ui_ng/package.json /harbor_ui
|
||||
COPY src/ui_ng/tslint.json /harbor_ui
|
||||
COPY src/ui_ng/typings.json /harbor_ui
|
||||
COPY src/ui_ng/yarn.lock /harbor_ui
|
||||
COPY make/dev/nodeclarity/angular-cli.json /harbor_ui
|
||||
COPY make/dev/nodeclarity/entrypoint.sh /
|
||||
|
||||
COPY src/ui_ng/tsconfig-aot.json /clarity-seed
|
||||
COPY src/ui_ng/rollup-config.js /clarity-seed
|
||||
COPY src/ui_ng/tsconfig-aot.json /harbor_ui
|
||||
COPY src/ui_ng/rollup-config.js /harbor_ui
|
||||
|
||||
WORKDIR /clarity-seed
|
||||
WORKDIR /harbor_ui
|
||||
|
||||
RUN npm __proxy__ install -g @angular/cli && \
|
||||
npm __proxy__ install && \
|
||||
chmod u+x /entrypoint.sh
|
||||
VOLUME ["/clarity-seed", "/clarity-seed/dist"]
|
||||
VOLUME ["/harbor_ui", "/harbor_ui/dist"]
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"project": {
|
||||
"version": "1.0.0-beta.20-4",
|
||||
"name": "clarity-seed"
|
||||
"version": "1.1.0",
|
||||
"name": "Harbor"
|
||||
},
|
||||
"apps": [
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
cd /clarity-seed
|
||||
cd /harbor_ui
|
||||
rm -rf dist/*
|
||||
|
||||
npm_proxy=
|
||||
|
@ -14,12 +14,28 @@
|
||||
<clr-icon shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem *ngFor="let f of filterOptions" (click)="toggleFilterOption(f.key)"><clr-icon shape="check" [hidden]="!f.checked"></clr-icon> {{f.description | translate}}</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem *ngFor="let f of filterOptions" (click)="toggleFilterOption(f.key)">
|
||||
<clr-icon shape="check" [hidden]="!f.checked"></clr-icon>
|
||||
<ng-template [ngIf]="!f.checked"><span style="display: inline-block;width: 16px;"></span></ng-template>
|
||||
{{f.description | translate}}
|
||||
</a>
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<div class="flex-xs-middle">
|
||||
<clr-icon shape="date"></clr-icon><input type="date" #fromTime (change)="doSearchByTimeRange(fromTime.value, 'begin')">
|
||||
<clr-icon shape="date"></clr-icon><input type="date" #toTime (change)="doSearchByTimeRange(toTime.value, 'end')">
|
||||
<clr-icon shape="date"></clr-icon>
|
||||
<label for="fromDateInput" aria-haspopup="true" role="tooltip" [class.invalid]="fromTimeInvalid" class="tooltip tooltip-validation invalid tooltip-sm">
|
||||
<input id="fromDateInput" type="date" #fromTime="ngModel" name="from" [(ngModel)]="queryParam.fromTime" dateValidator placeholder="dd/mm/yyyy" (change)="doSearchByStartTime(fromTime.value)">
|
||||
<span *ngIf="fromTimeInvalid" class="tooltip-content">
|
||||
{{'AUDIT_LOG.INVALID_DATE' | translate }}
|
||||
</span>
|
||||
</label>
|
||||
<clr-icon shape="date"></clr-icon>
|
||||
<label for="toDateInput" aria-haspopup="true" role="tooltip" [class.invalid]="toTimeInvalid" class="tooltip tooltip-validation invalid tooltip-sm">
|
||||
<input id="toDateInput" type="date" #toTime="ngModel" name="to" [(ngModel)]="queryParam.toTime" dateValidator placeholder="dd/mm/yyyy" (change)="doSearchByEndTime(toTime.value)">
|
||||
<span *ngIf="toTimeInvalid" class="tooltip-content">
|
||||
{{'AUDIT_LOG.INVALID_DATE' | translate }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,7 +11,8 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { NgModel } from '@angular/forms';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
|
||||
import { AuditLog } from './audit-log';
|
||||
@ -70,6 +71,17 @@ export class AuditLogComponent implements OnInit {
|
||||
totalRecordCount: number;
|
||||
totalPage: number;
|
||||
|
||||
@ViewChild('fromTime') fromTimeInput: NgModel;
|
||||
@ViewChild('toTime') toTimeInput: NgModel;
|
||||
|
||||
get fromTimeInvalid(): boolean {
|
||||
return this.fromTimeInput.errors && this.fromTimeInput.errors.dateValidator && (this.fromTimeInput.dirty || this.fromTimeInput.touched)
|
||||
}
|
||||
|
||||
get toTimeInvalid(): boolean {
|
||||
return this.toTimeInput.errors && this.toTimeInput.errors.dateValidator && (this.toTimeInput.dirty || this.toTimeInput.touched);
|
||||
}
|
||||
|
||||
constructor(private route: ActivatedRoute, private router: Router, private auditLogService: AuditLogService, private messageHandlerService: MessageHandlerService) {
|
||||
//Get current user from registered resolver.
|
||||
this.route.data.subscribe(data=>this.currentUser = <SessionUser>data['auditLogResolver']);
|
||||
@ -79,6 +91,7 @@ export class AuditLogComponent implements OnInit {
|
||||
this.projectId = +this.route.snapshot.parent.params['id'];
|
||||
this.queryParam.project_id = this.projectId;
|
||||
this.queryParam.page_size = this.pageSize;
|
||||
|
||||
}
|
||||
|
||||
retrieve(state?: State): void {
|
||||
@ -105,15 +118,29 @@ export class AuditLogComponent implements OnInit {
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
doSearchByTimeRange(strDate: string, target: string): void {
|
||||
let oneDayOffset = 3600 * 24;
|
||||
switch(target) {
|
||||
case 'begin':
|
||||
convertDate(strDate: string): string {
|
||||
if(/^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d$/.test(strDate)) {
|
||||
let parts = strDate.split(/[-\/]/);
|
||||
strDate = parts[2] /*Year*/ + '-' +parts[1] /*Month*/ + '-' + parts[0] /*Date*/;
|
||||
}
|
||||
return strDate;
|
||||
}
|
||||
|
||||
doSearchByStartTime(strDate: string): void {
|
||||
this.queryParam.begin_timestamp = 0;
|
||||
if(this.fromTimeInput.valid && strDate){
|
||||
strDate = this.convertDate(strDate);
|
||||
this.queryParam.begin_timestamp = new Date(strDate).getTime() / 1000;
|
||||
break;
|
||||
case 'end':
|
||||
}
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
doSearchByEndTime(strDate: string): void {
|
||||
this.queryParam.end_timestamp = 0;
|
||||
if(this.toTimeInput.valid && strDate) {
|
||||
strDate = this.convertDate(strDate);
|
||||
let oneDayOffset = 3600 * 24;
|
||||
this.queryParam.end_timestamp = new Date(strDate).getTime() / 1000 + oneDayOffset;
|
||||
break;
|
||||
}
|
||||
this.retrieve();
|
||||
}
|
||||
|
@ -42,4 +42,6 @@ export class AuditLog {
|
||||
keywords: string;
|
||||
page: number;
|
||||
page_size: number;
|
||||
fromTime: string;
|
||||
toTime: string;
|
||||
}
|
@ -21,7 +21,8 @@ import { RecentLogComponent } from './recent-log.component';
|
||||
imports: [SharedModule],
|
||||
declarations: [
|
||||
AuditLogComponent,
|
||||
RecentLogComponent],
|
||||
RecentLogComponent
|
||||
],
|
||||
providers: [AuditLogService],
|
||||
exports: [
|
||||
AuditLogComponent,
|
||||
|
@ -4,10 +4,10 @@
|
||||
<div class="modal-body" style="height: 12.8em; overflow-y: hidden;">
|
||||
<form #projectForm="ngForm">
|
||||
<section class="form-block">
|
||||
<div class="form-group">
|
||||
<label for="create_project_name" class="col-md-4 form-group-label-override">{{'PROJECT.NAME' | translate}}</label>
|
||||
<label for="create_project_name" aria-haspopup="true" role="tooltip" [class.invalid]="projectName.invalid && (projectName.dirty || projectName.touched)" [class.valid]="projectName.valid" class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left">
|
||||
<input type="text" id="create_project_name" [(ngModel)]="project.name" name="name" size="20" required minlength="2" #projectName="ngModel" targetExists="PROJECT_NAME">
|
||||
<div class="form-group" style="padding-left: 135px;">
|
||||
<label for="create_project_name" class="col-md-3 form-group-label-override">{{'PROJECT.NAME' | translate}}</label>
|
||||
<label for="create_project_name" aria-haspopup="true" role="tooltip" [class.invalid]="projectName.invalid && (projectName.dirty || projectName.touched)" class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left">
|
||||
<input type="text" id="create_project_name" [(ngModel)]="project.name" name="name" size="38" required minlength="2" #projectName="ngModel" targetExists="PROJECT_NAME">
|
||||
<span class="tooltip-content" *ngIf="projectName.errors && projectName.errors.required && (projectName.dirty || projectName.touched)">
|
||||
{{'PROJECT.NAME_IS_REQUIRED' | translate}}
|
||||
</span>
|
||||
@ -19,16 +19,17 @@
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 form-group-label-override">{{'PROJECT.PUBLIC_OR_PRIVATE' | translate}}</label>
|
||||
<div class="form-group" style="padding-left: 135px;">
|
||||
<label class="col-md-3 form-group-label-override">{{'PROJECT.ACCESS_LEVEL' | translate}}</label>
|
||||
<div class="checkbox-inline">
|
||||
<input type="checkbox" id="create_project_public" [(ngModel)]="project.public" name="public">
|
||||
<label for="create_project_public"></label>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-lg tooltip-right" style="top:-8px; left:-8px;">
|
||||
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
|
||||
<span class="tooltip-content" style="margin-left: 5px;">{{'PROJECT.INLINE_HELP_PUBLIC' | translate }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="inline-help-public">
|
||||
{{'PROJECT.INLINE_HELP_PUBLIC' | translate }}
|
||||
</p>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -1,12 +1,3 @@
|
||||
.inline-help-public {
|
||||
color: #CCCCCC;
|
||||
font-size: 12px;
|
||||
line-height: 1.5em;
|
||||
letter-spacing: 0.01em;
|
||||
margin-top: 0;
|
||||
padding: 0 14px;
|
||||
}
|
||||
|
||||
.form-group-label-override {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
||||
<clr-dg-column>{{'PROJECT.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.PUBLIC_OR_PRIVATE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.ACCESS_LEVEL' | translate}}</clr-dg-column>
|
||||
<clr-dg-column *ngIf="showRoleInfo">{{'PROJECT.ROLE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column>
|
||||
|
@ -5,8 +5,4 @@
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.datagrid-foot {
|
||||
margin-bottom: 0.5px;
|
||||
}
|
@ -19,9 +19,9 @@
|
||||
<clr-dg-column>{{'MEMBER.ROLE' | translate}}</clr-dg-column>
|
||||
<clr-dg-row *clrDgItems="let m of members">
|
||||
<clr-dg-action-overflow [hidden]="m.user_id === currentUser.user_id || !hasProjectAdminRole">
|
||||
<button class="action-item" (click)="changeRole(m, 1)">{{'MEMBER.PROJECT_ADMIN' | translate}}</button>
|
||||
<button class="action-item" (click)="changeRole(m, 2)">{{'MEMBER.DEVELOPER' | translate}}</button>
|
||||
<button class="action-item" (click)="changeRole(m, 3)">{{'MEMBER.GUEST' | translate}}</button>
|
||||
<button class="action-item" [hidden]="m.role_id === 1" (click)="changeRole(m, 1)">{{'MEMBER.PROJECT_ADMIN' | translate}}</button>
|
||||
<button class="action-item" [hidden]="m.role_id === 2" (click)="changeRole(m, 2)">{{'MEMBER.DEVELOPER' | translate}}</button>
|
||||
<button class="action-item" [hidden]="m.role_id === 3" (click)="changeRole(m, 3)">{{'MEMBER.GUEST' | translate}}</button>
|
||||
<button class="action-item" (click)="deleteMember(m)">{{'MEMBER.DELETE' | translate}}</button>
|
||||
</clr-dg-action-overflow>
|
||||
<clr-dg-cell>{{m.username}}</clr-dg-cell>
|
||||
|
@ -22,7 +22,7 @@ import { MemberService } from './member.service';
|
||||
import { AddMemberComponent } from './add-member/add-member.component';
|
||||
|
||||
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
||||
import { ConfirmationTargets, ConfirmationState } from '../../shared/shared.const';
|
||||
import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from '../../shared/shared.const';
|
||||
|
||||
import { ConfirmationDialogService } from '../../shared/confirmation-dialog/confirmation-dialog.service';
|
||||
import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message';
|
||||
@ -111,8 +111,8 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
ngOnInit() {
|
||||
//Get projectId from route params snapshot.
|
||||
this.projectId = +this.route.snapshot.parent.params['id'];
|
||||
this.currentUser = this.session.getCurrentUser();
|
||||
//Get current user from registered resolver.
|
||||
this.currentUser = this.session.getCurrentUser();
|
||||
let resolverData = this.route.snapshot.parent.data;
|
||||
if(resolverData) {
|
||||
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||
@ -149,7 +149,8 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
'MEMBER.DELETION_SUMMARY',
|
||||
m.username,
|
||||
m.user_id,
|
||||
ConfirmationTargets.PROJECT_MEMBER
|
||||
ConfirmationTargets.PROJECT_MEMBER,
|
||||
ConfirmationButtons.DELETE_CANCEL
|
||||
);
|
||||
this.deletionDialogService.openComfirmDialog(deletionMessage);
|
||||
}
|
||||
|
@ -7,6 +7,10 @@
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.subnav .nav {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.role-label {
|
||||
color: #CCCCCC;
|
||||
font-size: 14px;
|
||||
|
@ -13,7 +13,7 @@
|
||||
</div>
|
||||
<div class="option-right">
|
||||
<div class="select" style="float: left;">
|
||||
<select (change)="doFilterProjects($event)">
|
||||
<select (change)="doFilterProjects($event)">
|
||||
<option value="0">{{projectTypes[0] | translate}}</option>
|
||||
<option value="1">{{projectTypes[1] | translate}}</option>
|
||||
</select>
|
||||
|
@ -29,7 +29,7 @@ import { Response } from '@angular/http';
|
||||
|
||||
import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service';
|
||||
import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message';
|
||||
import { ConfirmationTargets, ConfirmationState } from '../shared/shared.const';
|
||||
import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from '../shared/shared.const';
|
||||
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
@ -178,7 +178,8 @@ export class ProjectComponent implements OnInit, OnDestroy {
|
||||
'PROJECT.DELETION_SUMMARY',
|
||||
p.name,
|
||||
p.project_id,
|
||||
ConfirmationTargets.PROJECT
|
||||
ConfirmationTargets.PROJECT,
|
||||
ConfirmationButtons.DELETE_CANCEL
|
||||
);
|
||||
this.deletionDialogService.openComfirmDialog(deletionMessage);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import { MessageHandlerService } from '../../shared/message-handler/message-hand
|
||||
import { ConfirmationDialogService } from '../../shared/confirmation-dialog/confirmation-dialog.service';
|
||||
import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message';
|
||||
|
||||
import { ConfirmationTargets, ConfirmationState } from '../../shared/shared.const';
|
||||
import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from '../../shared/shared.const';
|
||||
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
@ -149,7 +149,8 @@ export class DestinationComponent implements OnInit {
|
||||
'REPLICATION.DELETION_SUMMARY_TARGET',
|
||||
target.name,
|
||||
target.id,
|
||||
ConfirmationTargets.TARGET);
|
||||
ConfirmationTargets.TARGET,
|
||||
ConfirmationButtons.DELETE_CANCEL);
|
||||
this.deletionDialogService.openComfirmDialog(deletionMessage);
|
||||
}
|
||||
}
|
||||
|
@ -5,4 +5,8 @@
|
||||
|
||||
.sub-nav-bg-color {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.subnav .nav {
|
||||
padding-left: 0;
|
||||
}
|
@ -1,58 +1,62 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="flex-xs-middle option-left">
|
||||
<button class="btn btn-link" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'REPLICATION.REPLICATION_RULE' | translate}}</button>
|
||||
<create-edit-policy [projectId]="projectId" (reload)="reloadPolicies($event)"></create-edit-policy>
|
||||
</div>
|
||||
<div class="flex-xs-middle option-right">
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-left'">
|
||||
<button class="btn btn-link" clrDropdownToggle>
|
||||
{{currentRuleStatus.description | translate}}
|
||||
<clr-icon shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem *ngFor="let r of ruleStatus" (click)="doFilterPolicyStatus(r.key)"> {{r.description | translate}}</a>
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<grid-filter filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchPolicies($event)" [currentValue]="search.policyName"></grid-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshPolicies()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="flex-xs-middle option-left">
|
||||
<button class="btn btn-link" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'REPLICATION.REPLICATION_RULE' | translate}}</button>
|
||||
<create-edit-policy [projectId]="projectId" (reload)="reloadPolicies($event)"></create-edit-policy>
|
||||
</div>
|
||||
<div class="flex-xs-middle option-right">
|
||||
<div class="select" style="float: left;">
|
||||
<select (change)="doFilterPolicyStatus($event)">
|
||||
<option *ngFor="let r of ruleStatus" value="{{r.key}}">{{r.description | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<grid-filter filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchPolicies($event)" [currentValue]="search.policyName"></grid-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshPolicies()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<list-policy [policies]="changedPolicies" [projectless]="false" [selectedId]="initSelectedId" (selectOne)="selectOnePolicy($event)" (editOne)="openEditPolicy($event)" (reload)="reloadPolicies($event)"></list-policy>
|
||||
<list-policy [policies]="changedPolicies" [projectless]="false" [selectedId]="initSelectedId" (selectOne)="selectOnePolicy($event)" (editOne)="openEditPolicy($event)" (reload)="reloadPolicies($event)"></list-policy>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<h5 class="flex-items-xs-bottom option-left-down" style="margin-left: 14px;">{{'REPLICATION.REPLICATION_JOBS' | translate}}</h5>
|
||||
<div class="flex-items-xs-bottom option-right-down">
|
||||
<button class="btn btn-link" (click)="toggleSearchJobOptionalName(currentJobSearchOption)">{{toggleJobSearchOption[currentJobSearchOption] | translate}}</button>
|
||||
<grid-filter filterPlaceholder='{{"REPLICATION.FILTER_JOBS_PLACEHOLDER" | translate}}' (filter)="doSearchJobs($event)" [currentValue]="search.repoName" ></grid-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshJobs()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row flex-items-xs-between">
|
||||
<h5 class="flex-items-xs-bottom option-left-down" style="margin-left: 14px;">{{'REPLICATION.REPLICATION_JOBS' | translate}}</h5>
|
||||
<div class="flex-items-xs-bottom option-right-down">
|
||||
<button class="btn btn-link" (click)="toggleSearchJobOptionalName(currentJobSearchOption)">{{toggleJobSearchOption[currentJobSearchOption] | translate}}</button>
|
||||
<grid-filter filterPlaceholder='{{"REPLICATION.FILTER_JOBS_PLACEHOLDER" | translate}}' (filter)="doSearchJobs($event)" [currentValue]="search.repoName" ></grid-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshJobs()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row flex-items-xs-right option-right" [hidden]="currentJobSearchOption === 0">
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-left'">
|
||||
<button class="btn btn-link" clrDropdownToggle>
|
||||
{{currentJobStatus.description | translate}}
|
||||
<clr-icon shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem *ngFor="let j of jobStatus" (click)="doFilterJobStatus(j.key)"> {{j.description | translate}}</a>
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<div class="flex-items-xs-middle">
|
||||
<clr-icon shape="date"></clr-icon><input type="date" #fromTime (change)="doJobSearchByStartTime(fromTime.value)">
|
||||
<clr-icon shape="date"></clr-icon><input type="date" #toTime (change)="doJobSearchByEndTime(toTime.value)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row flex-items-xs-right option-right" [hidden]="currentJobSearchOption === 0">
|
||||
<div class="select" style="float: left;">
|
||||
<select (change)="doFilterJobStatus($event)">
|
||||
<option *ngFor="let j of jobStatus" value="{{j.key}}" [selected]="currentJobStatus.key === j.key">{{j.description | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-items-xs-middle">
|
||||
<clr-icon shape="date"></clr-icon>
|
||||
<label for="fromDateInput" aria-haspopup="true" role="tooltip" [class.invalid]="fromTimeInvalid" class="tooltip tooltip-validation invalid tooltip-sm">
|
||||
<input id="fromDateInput" type="date" #fromTime="ngModel" name="from" [(ngModel)]="search.startTime" dateValidator placeholder="dd/mm/yyyy" (change)="doJobSearchByStartTime(fromTime.value)">
|
||||
<span *ngIf="fromTimeInvalid" class="tooltip-content">
|
||||
{{'AUDIT_LOG.INVALID_DATE' | translate }}
|
||||
</span>
|
||||
</label>
|
||||
<clr-icon shape="date"></clr-icon>
|
||||
<label for="toDateInput" aria-haspopup="true" role="tooltip" [class.invalid]="toTimeInvalid" class="tooltip tooltip-validation invalid tooltip-sm">
|
||||
<input id="toDateInput" type="date" #toTime="ngModel" name="to" [(ngModel)]="search.endTime" dateValidator placeholder="dd/mm/yyyy" (change)="doJobSearchByEndTime(toTime.value)">
|
||||
<span *ngIf="toTimeInvalid" class="tooltip-content">
|
||||
{{'AUDIT_LOG.INVALID_DATE' | translate }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<list-job [jobs]="changedJobs" [totalPage]="jobsTotalPage" [totalRecordCount]="jobsTotalRecordCount" (paginate)="fetchPolicyJobs($event)"></list-job>
|
||||
<list-job [jobs]="changedJobs" [totalPage]="jobsTotalPage" [totalRecordCount]="jobsTotalRecordCount" (paginate)="fetchPolicyJobs($event)"></list-job>
|
||||
</div>
|
||||
</div>
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NgModel } from '@angular/forms';
|
||||
|
||||
import { CreateEditPolicyComponent } from '../shared/create-edit-policy/create-edit-policy.component';
|
||||
|
||||
@ -20,6 +21,7 @@ import { MessageHandlerService } from '../shared/message-handler/message-handler
|
||||
|
||||
import { ReplicationService } from './replication.service';
|
||||
|
||||
import { SessionUser } from '../shared/session-user';
|
||||
import { Policy } from './policy';
|
||||
import { Job } from './job';
|
||||
import { Target } from './target';
|
||||
@ -27,13 +29,13 @@ import { Target } from './target';
|
||||
import { State } from 'clarity-angular';
|
||||
|
||||
const ruleStatus = [
|
||||
{ 'key': '', 'description': 'REPLICATION.ALL_STATUS'},
|
||||
{ 'key': 'all', 'description': 'REPLICATION.ALL_STATUS'},
|
||||
{ 'key': '1', 'description': 'REPLICATION.ENABLED'},
|
||||
{ 'key': '0', 'description': 'REPLICATION.DISABLED'}
|
||||
];
|
||||
|
||||
const jobStatus = [
|
||||
{ 'key': '', 'description': 'REPLICATION.ALL' },
|
||||
{ 'key': 'all', 'description': 'REPLICATION.ALL' },
|
||||
{ 'key': 'pending', 'description': 'REPLICATION.PENDING' },
|
||||
{ 'key': 'running', 'description': 'REPLICATION.RUNNING' },
|
||||
{ 'key': 'error', 'description': 'REPLICATION.ERROR' },
|
||||
@ -51,7 +53,9 @@ class SearchOption {
|
||||
repoName: string = '';
|
||||
status: string = '';
|
||||
startTime: string = '';
|
||||
startTimestamp: string = '';
|
||||
endTime: string = '';
|
||||
endTimestamp: string = '';
|
||||
page: number = 1;
|
||||
pageSize: number = 5;
|
||||
}
|
||||
@ -62,10 +66,10 @@ class SearchOption {
|
||||
styleUrls: ['./replication.component.css']
|
||||
})
|
||||
export class ReplicationComponent implements OnInit {
|
||||
|
||||
|
||||
projectId: number;
|
||||
|
||||
search: SearchOption;
|
||||
search: SearchOption = new SearchOption();
|
||||
|
||||
ruleStatus = ruleStatus;
|
||||
currentRuleStatus: {key: string, description: string};
|
||||
@ -89,15 +93,25 @@ export class ReplicationComponent implements OnInit {
|
||||
@ViewChild(CreateEditPolicyComponent)
|
||||
createEditPolicyComponent: CreateEditPolicyComponent;
|
||||
|
||||
@ViewChild('fromTime') fromTimeInput: NgModel;
|
||||
@ViewChild('toTime') toTimeInput: NgModel;
|
||||
|
||||
get fromTimeInvalid(): boolean {
|
||||
return this.fromTimeInput.errors && this.fromTimeInput.errors.dateValidator && (this.fromTimeInput.dirty || this.fromTimeInput.touched);
|
||||
}
|
||||
|
||||
get toTimeInvalid(): boolean {
|
||||
return this.toTimeInput.errors && this.toTimeInput.errors.dateValidator && (this.toTimeInput.dirty || this.toTimeInput.touched);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
private replicationService: ReplicationService,
|
||||
private route: ActivatedRoute) {
|
||||
private route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.projectId = +this.route.snapshot.parent.params['id'];
|
||||
this.search = new SearchOption();
|
||||
this.currentRuleStatus = this.ruleStatus[0];
|
||||
this.currentJobStatus = this.jobStatus[0];
|
||||
this.currentJobSearchOption = 0;
|
||||
@ -148,7 +162,7 @@ export class ReplicationComponent implements OnInit {
|
||||
}
|
||||
this.replicationService
|
||||
.listJobs(this.search.policyId, this.search.status, this.search.repoName,
|
||||
this.search.startTime, this.search.endTime, this.search.page, this.search.pageSize)
|
||||
this.search.startTimestamp, this.search.endTimestamp, this.search.page, this.search.pageSize)
|
||||
.subscribe(
|
||||
response=>{
|
||||
this.jobsTotalRecordCount = response.headers.get('x-total-count');
|
||||
@ -171,9 +185,9 @@ export class ReplicationComponent implements OnInit {
|
||||
if(policy) {
|
||||
this.search.policyId = policy.id;
|
||||
this.search.repoName = '';
|
||||
this.search.status = ''
|
||||
this.search.status = '';
|
||||
this.currentJobSearchOption = 0;
|
||||
this.currentJobStatus = { 'key': '', 'description': 'REPLICATION.ALL'};
|
||||
this.currentJobStatus = { 'key': 'all', 'description': 'REPLICATION.ALL' };
|
||||
this.fetchPolicyJobs();
|
||||
}
|
||||
}
|
||||
@ -183,19 +197,28 @@ export class ReplicationComponent implements OnInit {
|
||||
this.retrievePolicies();
|
||||
}
|
||||
|
||||
doFilterPolicyStatus(status: string) {
|
||||
this.currentRuleStatus = this.ruleStatus.find(r=>r.key === status);
|
||||
if(status.trim() === '') {
|
||||
this.changedPolicies = this.policies;
|
||||
} else {
|
||||
this.changedPolicies = this.policies.filter(policy=>policy.enabled === +this.currentRuleStatus.key);
|
||||
doFilterPolicyStatus($event: any) {
|
||||
if ($event && $event.target && $event.target["value"]) {
|
||||
let status = $event.target["value"];
|
||||
this.currentRuleStatus = this.ruleStatus.find(r=>r.key === status);
|
||||
if(this.currentRuleStatus.key === 'all') {
|
||||
this.changedPolicies = this.policies;
|
||||
} else {
|
||||
this.changedPolicies = this.policies.filter(policy=>policy.enabled === +this.currentRuleStatus.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doFilterJobStatus(status: string) {
|
||||
this.currentJobStatus = this.jobStatus.find(r=>r.key === status);
|
||||
this.search.status = status;
|
||||
this.doSearchJobs(this.search.repoName);
|
||||
doFilterJobStatus($event: any) {
|
||||
if ($event && $event.target && $event.target["value"]) {
|
||||
let status = $event.target["value"];
|
||||
this.currentJobStatus = this.jobStatus.find(r=>r.key === status);
|
||||
if(this.currentJobStatus.key === 'all') {
|
||||
status = '';
|
||||
}
|
||||
this.search.status = status;
|
||||
this.doSearchJobs(this.search.repoName);
|
||||
}
|
||||
}
|
||||
|
||||
doSearchJobs(repoName: string) {
|
||||
@ -222,20 +245,30 @@ export class ReplicationComponent implements OnInit {
|
||||
(option === 1) ? this.currentJobSearchOption = 0 : this.currentJobSearchOption = 1;
|
||||
}
|
||||
|
||||
convertDate(strDate: string): string {
|
||||
if(/^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d$/.test(strDate)) {
|
||||
let parts = strDate.split(/[-\/]/);
|
||||
strDate = parts[2] /*Year*/ + '-' +parts[1] /*Month*/ + '-' + parts[0] /*Date*/;
|
||||
}
|
||||
return strDate;
|
||||
}
|
||||
|
||||
doJobSearchByStartTime(strDate: string) {
|
||||
if(!strDate || strDate.trim() === '') {
|
||||
strDate = 0 + '';
|
||||
}
|
||||
(strDate === '0') ? this.search.startTime = '' : this.search.startTime = (new Date(strDate).getTime() / 1000) + '';
|
||||
this.search.startTimestamp = '';
|
||||
if(this.fromTimeInput.valid && strDate) {
|
||||
strDate = this.convertDate(strDate);
|
||||
this.search.startTimestamp = new Date(strDate).getTime() / 1000 + '';
|
||||
}
|
||||
this.fetchPolicyJobs();
|
||||
}
|
||||
|
||||
doJobSearchByEndTime(strDate: string) {
|
||||
if(!strDate || strDate.trim() === '') {
|
||||
strDate = 0 + '';
|
||||
this.search.endTimestamp = '';
|
||||
if(this.toTimeInput.valid && strDate) {
|
||||
strDate = this.convertDate(strDate);
|
||||
let oneDayOffset = 3600 * 24;
|
||||
this.search.endTimestamp = (new Date(strDate).getTime() / 1000 + oneDayOffset) + '';
|
||||
}
|
||||
let oneDayOffset = 3600 * 24;
|
||||
(strDate === '0') ? this.search.endTime = '' : this.search.endTime = (new Date(strDate).getTime() / 1000 + oneDayOffset) + '';
|
||||
this.fetchPolicyJobs();
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ import { RepositoryService } from './repository.service';
|
||||
import { Repository } from './repository';
|
||||
|
||||
import { MessageHandlerService } from '../shared/message-handler/message-handler.service';
|
||||
import { ConfirmationState, ConfirmationTargets } from '../shared/shared.const';
|
||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
||||
|
||||
|
||||
import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service';
|
||||
@ -115,7 +115,8 @@ export class RepositoryComponent implements OnInit {
|
||||
'REPOSITORY.DELETION_SUMMARY_REPO',
|
||||
repoName,
|
||||
repoName,
|
||||
ConfirmationTargets.REPOSITORY);
|
||||
ConfirmationTargets.REPOSITORY,
|
||||
ConfirmationButtons.DELETE_CANCEL);
|
||||
this.deletionDialogService.openComfirmDialog(message);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { RepositoryService } from '../repository.service';
|
||||
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
||||
import { ConfirmationTargets, ConfirmationState } from '../../shared/shared.const';
|
||||
import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from '../../shared/shared.const';
|
||||
|
||||
import { ConfirmationDialogService } from '../../shared/confirmation-dialog/confirmation-dialog.service';
|
||||
import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message';
|
||||
@ -155,25 +155,25 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
||||
|
||||
deleteTag(tag: TagView) {
|
||||
if (tag) {
|
||||
let titleKey: string, summaryKey: string, content: string, confirmOnly: boolean;
|
||||
let titleKey: string, summaryKey: string, content: string, buttons: ConfirmationButtons;
|
||||
if (tag.signed) {
|
||||
titleKey = 'REPOSITORY.DELETION_TITLE_TAG_DENIED';
|
||||
summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG_DENIED';
|
||||
confirmOnly = true;
|
||||
buttons = ConfirmationButtons.CLOSE;
|
||||
content = 'notary -s https://' + this.registryUrl + ':4443 -d ~/.docker/trust remove -p ' + this.registryUrl + '/' + this.repoName + ' ' + tag.tag;
|
||||
} else {
|
||||
titleKey = 'REPOSITORY.DELETION_TITLE_TAG';
|
||||
summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG';
|
||||
buttons = ConfirmationButtons.DELETE_CANCEL;
|
||||
content = tag.tag;
|
||||
confirmOnly = false;
|
||||
}
|
||||
let message = new ConfirmationMessage(
|
||||
titleKey,
|
||||
summaryKey,
|
||||
content,
|
||||
tag,
|
||||
ConfirmationTargets.TAG);
|
||||
message.confirmOnly = confirmOnly;
|
||||
ConfirmationTargets.TAG,
|
||||
buttons);
|
||||
this.deletionDialogService.openComfirmDialog(message);
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,21 @@
|
||||
</div>
|
||||
<div class="confirmation-content">{{dialogContent}}</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" *ngIf="!confirmOnly" (click)="cancel()">{{'BUTTON.NO' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{ buttonKey | translate}}</button>
|
||||
<div class="modal-footer" [ngSwitch]="buttons">
|
||||
<ng-template [ngSwitchCase]="0">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{ 'BUTTON.CONFIRM' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="1">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.NO' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{ 'BUTTON.YES' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="2">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="confirm()">{{ 'BUTTON.DELETE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="3">
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</clr-modal>
|
@ -18,7 +18,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { ConfirmationDialogService } from './confirmation-dialog.service';
|
||||
import { ConfirmationMessage } from './confirmation-message';
|
||||
import { ConfirmationAcknowledgement } from './confirmation-state-message';
|
||||
import { ConfirmationState, ConfirmationTargets } from '../shared.const';
|
||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared.const';
|
||||
|
||||
@Component({
|
||||
selector: 'confiramtion-dialog',
|
||||
@ -30,10 +30,9 @@ export class ConfirmationDialogComponent implements OnDestroy {
|
||||
opened: boolean = false;
|
||||
dialogTitle: string = "";
|
||||
dialogContent: string = "";
|
||||
buttonKey: string = 'BUTTON.OK';
|
||||
confirmOnly: boolean = false;
|
||||
message: ConfirmationMessage;
|
||||
annouceSubscription: Subscription;
|
||||
buttons: ConfirmationButtons;
|
||||
|
||||
constructor(
|
||||
private confirmationService: ConfirmationDialogService,
|
||||
@ -42,11 +41,10 @@ export class ConfirmationDialogComponent implements OnDestroy {
|
||||
this.dialogTitle = msg.title;
|
||||
this.dialogContent = msg.message;
|
||||
this.message = msg;
|
||||
this.confirmOnly = this.message.confirmOnly;
|
||||
this.buttonKey = this.confirmOnly ? 'BUTTON.CLOSE' : 'BUTTON.OK';
|
||||
this.translate.get(this.dialogTitle).subscribe((res: string) => this.dialogTitle = res);
|
||||
this.translate.get(this.dialogContent, { 'param': msg.param }).subscribe((res: string) => this.dialogContent = res);
|
||||
//Open dialog
|
||||
this.buttons = msg.buttons;
|
||||
this.open();
|
||||
});
|
||||
}
|
||||
|
@ -11,20 +11,21 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { ConfirmationTargets } from '../../shared/shared.const';
|
||||
import { ConfirmationTargets, ConfirmationButtons } from '../../shared/shared.const';
|
||||
|
||||
export class ConfirmationMessage {
|
||||
public constructor(title: string, message: string, param: string, data: any, targetId: ConfirmationTargets) {
|
||||
public constructor(title: string, message: string, param: string, data: any, targetId: ConfirmationTargets, buttons?: ConfirmationButtons) {
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
this.targetId = targetId;
|
||||
this.param = param;
|
||||
this.buttons = buttons ? buttons : ConfirmationButtons.CONFIRM_CANCEL;
|
||||
}
|
||||
title: string;
|
||||
message: string;
|
||||
data: any = {};//default is empty
|
||||
targetId: ConfirmationTargets = ConfirmationTargets.EMPTY;
|
||||
param: string;
|
||||
confirmOnly: boolean;
|
||||
buttons: ConfirmationButtons;
|
||||
}
|
50
src/ui_ng/src/app/shared/date-validator.directive.ts
Normal file
50
src/ui_ng/src/app/shared/date-validator.directive.ts
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Directive, OnChanges, Input, SimpleChanges } from '@angular/core';
|
||||
import { NG_VALIDATORS, Validator, Validators, ValidatorFn, AbstractControl } from '@angular/forms';
|
||||
|
||||
@Directive({
|
||||
selector: '[dateValidator]',
|
||||
providers: [{provide: NG_VALIDATORS, useExisting: DateValidatorDirective, multi: true}]
|
||||
})
|
||||
export class DateValidatorDirective implements Validator, OnChanges {
|
||||
@Input() dateValidator: string;
|
||||
private valFn = Validators.nullValidator;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
const change = changes['dateValidator'];
|
||||
if (change) {
|
||||
this.valFn = dateValidator();
|
||||
} else {
|
||||
this.valFn = Validators.nullValidator;
|
||||
}
|
||||
}
|
||||
validate(control: AbstractControl): {[key: string]: any} {
|
||||
return this.valFn(control);
|
||||
}
|
||||
}
|
||||
|
||||
export function dateValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): {[key: string]: any} => {
|
||||
let controlValue = control.value;
|
||||
let valid = true;
|
||||
if(controlValue) {
|
||||
const regYMD=/^(19|20)\d\d([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])$/g;
|
||||
const regDMY=/^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d$/g;
|
||||
valid = (regYMD.test(controlValue) || regDMY.test(controlValue));
|
||||
}
|
||||
return valid ? null : {'dateValidator': { value: controlValue }};
|
||||
};
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import { Policy } from '../../replication/policy';
|
||||
import { ConfirmationDialogService } from '../../shared/confirmation-dialog/confirmation-dialog.service';
|
||||
import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message';
|
||||
|
||||
import { ConfirmationState, ConfirmationTargets } from '../../shared/shared.const';
|
||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../../shared/shared.const';
|
||||
|
||||
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
||||
|
||||
@ -135,7 +135,8 @@ export class ListPolicyComponent implements OnDestroy {
|
||||
'REPLICATION.DELETION_SUMMARY',
|
||||
policy.name,
|
||||
policy.id,
|
||||
ConfirmationTargets.POLICY);
|
||||
ConfirmationTargets.POLICY,
|
||||
ConfirmationButtons.DELETE_CANCEL);
|
||||
this.deletionDialogService.openComfirmDialog(deletionMessage);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
||||
<clr-dg-column>{{'PROJECT.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.PUBLIC_OR_PRIVATE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.ACCESS_LEVEL' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-row *clrDgItems="let p of projects" [clrDgItem]="p">
|
||||
|
@ -67,6 +67,9 @@ export const CookieKeyOfAdmiral = "admiral.endpoint.latest";
|
||||
export const enum ConfirmationState {
|
||||
NA, CONFIRMED, CANCEL
|
||||
}
|
||||
export const enum ConfirmationButtons {
|
||||
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE
|
||||
}
|
||||
|
||||
export const ProjectTypes = { 0: 'PROJECT.MY_PROJECTS', 1: 'PROJECT.PUBLIC_PROJECTS' };
|
||||
export const RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' };
|
||||
|
@ -54,6 +54,7 @@ import { MessageHandlerService } from './message-handler/message-handler.service
|
||||
import { EmailValidatorDirective } from './email.directive';
|
||||
import { GaugeComponent } from './gauge/gauge.component';
|
||||
import { StatisticHandler } from './statictics/statistic-handler.service';
|
||||
import { DateValidatorDirective } from '../shared/date-validator.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -78,7 +79,8 @@ import { StatisticHandler } from './statictics/statistic-handler.service';
|
||||
ListProjectROComponent,
|
||||
ListRepositoryROComponent,
|
||||
EmailValidatorDirective,
|
||||
GaugeComponent
|
||||
GaugeComponent,
|
||||
DateValidatorDirective
|
||||
],
|
||||
exports: [
|
||||
CoreModule,
|
||||
@ -99,7 +101,8 @@ import { StatisticHandler } from './statictics/statistic-handler.service';
|
||||
ListProjectROComponent,
|
||||
ListRepositoryROComponent,
|
||||
EmailValidatorDirective,
|
||||
GaugeComponent
|
||||
GaugeComponent,
|
||||
DateValidatorDirective
|
||||
],
|
||||
providers: [
|
||||
SessionService,
|
||||
|
@ -21,7 +21,7 @@ import { NewUserModalComponent } from './new-user-modal.component';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service';
|
||||
import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message';
|
||||
import { ConfirmationState, ConfirmationTargets } from '../shared/shared.const'
|
||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const'
|
||||
import { MessageHandlerService } from '../shared/message-handler/message-handler.service';
|
||||
|
||||
import { SessionService } from '../shared/session.service';
|
||||
@ -188,7 +188,8 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
"USER.DELETION_SUMMARY",
|
||||
user.username,
|
||||
user,
|
||||
ConfirmationTargets.USER
|
||||
ConfirmationTargets.USER,
|
||||
ConfirmationButtons.DELETE_CANCEL
|
||||
);
|
||||
this.deletionDialogService.openComfirmDialog(msg);
|
||||
}
|
||||
|
@ -124,6 +124,7 @@
|
||||
"NAME": "Project Name",
|
||||
"ROLE": "Role",
|
||||
"PUBLIC_OR_PRIVATE": "Public",
|
||||
"ACCESS_LEVEL": "Access Level",
|
||||
"REPO_COUNT": "Repositories Count",
|
||||
"CREATION_TIME": "Creation Time",
|
||||
"PUBLIC": "Public",
|
||||
@ -197,7 +198,8 @@
|
||||
"ADVANCED": "Advanced",
|
||||
"SIMPLE": "Simple",
|
||||
"ITEMS": "item(s)",
|
||||
"FILTER_PLACEHOLDER": "Filter Logs"
|
||||
"FILTER_PLACEHOLDER": "Filter Logs",
|
||||
"INVALID_DATE": "Invalid date."
|
||||
},
|
||||
"REPLICATION": {
|
||||
"REPLICATION_RULE": "Replication Rule",
|
||||
@ -265,7 +267,8 @@
|
||||
"CANNOT_EDIT": "Replication rule cannot be changed while it is enabled.",
|
||||
"POLICY_ALREADY_EXISTS": "Replication rule already exists.",
|
||||
"FAILED_TO_DELETE_POLICY_ENABLED": "Cannot delete rule: rule has unfinished job(s) or rule is enabled.",
|
||||
"FOUND_ERROR_IN_JOBS": "Found errors in the replication job(s), please check."
|
||||
"FOUND_ERROR_IN_JOBS": "Found errors in the replication job(s), please check.",
|
||||
"INVALID_DATE": "Invalid date."
|
||||
},
|
||||
"DESTINATION": {
|
||||
"NEW_ENDPOINT": "New Endpoint",
|
||||
|
@ -124,6 +124,7 @@
|
||||
"NAME": "Nombre del Proyecto",
|
||||
"ROLE": "Rol",
|
||||
"PUBLIC_OR_PRIVATE": "Público",
|
||||
"ACCESS_LEVEL": "Nivel de acceso",
|
||||
"REPO_COUNT": "Contador de repositorios",
|
||||
"CREATION_TIME": "Fecha de creación",
|
||||
"PUBLIC": "Público",
|
||||
@ -197,7 +198,8 @@
|
||||
"ADVANCED": "Avanzado",
|
||||
"SIMPLE": "Simple",
|
||||
"ITEMS": "elemento(s)",
|
||||
"FILTER_PLACEHOLDER": "Filtrar logs"
|
||||
"FILTER_PLACEHOLDER": "Filtrar logs",
|
||||
"INVALID_DATE": "Fecha invalida."
|
||||
},
|
||||
"REPLICATION": {
|
||||
"REPLICATION_RULE": "Reglas de Replicación",
|
||||
@ -265,7 +267,8 @@
|
||||
"CANNOT_EDIT": "La regla de replicación no se puede cambiar mientras esté activa.",
|
||||
"POLICY_ALREADY_EXISTS": "La regla de replicación ya existe.",
|
||||
"FAILED_TO_DELETE_POLICY_ENABLED": "No se puede eliminar la regla: tiene trabajo(s) sin finalizar o está activa.",
|
||||
"FOUND_ERROR_IN_JOBS": "Se han encontrado errores en el trabajo de replicación. Por favor, compruébelos."
|
||||
"FOUND_ERROR_IN_JOBS": "Se han encontrado errores en el trabajo de replicación. Por favor, compruébelos.",
|
||||
"INVALID_DATE": "Fecha invalida."
|
||||
},
|
||||
"DESTINATION": {
|
||||
"NEW_ENDPOINT": "Nuevo Endpoint",
|
||||
|
@ -124,6 +124,7 @@
|
||||
"NAME": "项目名称",
|
||||
"ROLE": "角色",
|
||||
"PUBLIC_OR_PRIVATE": "公开",
|
||||
"ACCESS_LEVEL": "访问级别",
|
||||
"REPO_COUNT": "镜像仓库数",
|
||||
"CREATION_TIME": "创建时间",
|
||||
"PUBLIC": "公开",
|
||||
@ -197,7 +198,8 @@
|
||||
"ADVANCED": "高级检索",
|
||||
"SIMPLE": "简单检索",
|
||||
"ITEMS": "条记录",
|
||||
"FILTER_PLACEHOLDER": "过滤日志"
|
||||
"FILTER_PLACEHOLDER": "过滤日志",
|
||||
"INVALID_DATE": "无效日期。"
|
||||
},
|
||||
"REPLICATION": {
|
||||
"REPLICATION_RULE": "复制规则",
|
||||
@ -265,7 +267,8 @@
|
||||
"CANNOT_EDIT": "当复制规则启用时无法修改。",
|
||||
"POLICY_ALREADY_EXISTS": "规则已存在。",
|
||||
"FAILED_TO_DELETE_POLICY_ENABLED": "删除复制规则失败: 仍有未完成的任务或规则未停用。",
|
||||
"FOUND_ERROR_IN_JOBS": "复制任务中包含错误,请检查。"
|
||||
"FOUND_ERROR_IN_JOBS": "复制任务中包含错误,请检查。",
|
||||
"INVALID_DATE": "无效日期。"
|
||||
},
|
||||
"DESTINATION": {
|
||||
"NEW_ENDPOINT": "新建目标",
|
||||
|
Loading…
Reference in New Issue
Block a user