mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-20 09:15:19 +01:00
Merge branch 'harbor-clarity' into feature/seesion_service_#1369
This commit is contained in:
commit
dfabdf5475
@ -1,40 +1,41 @@
|
||||
<clr-main-container>
|
||||
<clr-modal [(clrModalOpen)]="account_settings_opened">
|
||||
<h3 class="modal-title">Accout Settings</h3>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<section class="form-block">
|
||||
<div class="form-group">
|
||||
<label for="account_settings_username" class="col-md-4">Username</label>
|
||||
<input type="text" class="col-md-8" id="account_settings_username" size="20">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="account_settings_email" class="col-md-4">Email</label>
|
||||
<input type="text" class="col-md-8" id="account_settings_email" size="20">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="account_settings_full_name" class="col-md-4">Full name</label>
|
||||
<input type="text" class="col-md-8" id="account_settings_full_name" size="20">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="account_settings_comments" class="col-md-4">Comments</label>
|
||||
<input type="text" class="col-md-8" id="account_settings_comments" size="20">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="button" class="col-md-4" class="btn btn-outline">Change Password</button>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="account_settings_opened = false">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" (click)="account_settings_opened = false">Ok</button>
|
||||
</div>
|
||||
</clr-modal>
|
||||
<navigator></navigator>
|
||||
<div class="content-container">
|
||||
<div class="content-area">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<clr-modal [(clrModalOpen)]="account_settings_opened">
|
||||
<h3 class="modal-title">Accout Settings</h3>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<section class="form-block">
|
||||
<div class="form-group">
|
||||
<label for="account_settings_username" class="col-md-4">Username</label>
|
||||
<input type="text" class="col-md-8" id="account_settings_username" size="20">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="account_settings_email" class="col-md-4">Email</label>
|
||||
<input type="text" class="col-md-8" id="account_settings_email" size="20">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="account_settings_full_name" class="col-md-4">Full name</label>
|
||||
<input type="text" class="col-md-8" id="account_settings_full_name" size="20">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="account_settings_comments" class="col-md-4">Comments</label>
|
||||
<input type="text" class="col-md-8" id="account_settings_comments" size="20">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="button" class="col-md-4" class="btn btn-outline">Change Password</button>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
</clr-main-container>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="account_settings_opened = false">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" (click)="account_settings_opened = false">Ok</button>
|
||||
</div>
|
||||
</clr-modal>
|
||||
<navigator></navigator>
|
||||
<global-message></global-message>
|
||||
<div class="content-container">
|
||||
<div class="content-area">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</clr-main-container>
|
||||
|
7
harbor-app/src/app/global-message/message.component.html
Normal file
7
harbor-app/src/app/global-message/message.component.html
Normal file
@ -0,0 +1,7 @@
|
||||
<clr-alert [clrAlertType]="'alert-danger'" [clrAlertAppLevel]="true" [(clrAlertClosed)]="!globalMessageOpened" (clrAlertClosedChange)="onClose()">
|
||||
<div class="alert-item">
|
||||
<span class="alert-text">
|
||||
{{globalMessage}}
|
||||
</span>
|
||||
</div>
|
||||
</clr-alert>
|
26
harbor-app/src/app/global-message/message.component.ts
Normal file
26
harbor-app/src/app/global-message/message.component.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { MessageService } from './message.service';
|
||||
|
||||
@Component({
|
||||
selector: 'global-message',
|
||||
templateUrl: 'message.component.html'
|
||||
})
|
||||
export class MessageComponent {
|
||||
|
||||
globalMessageOpened: boolean;
|
||||
globalMessage: string;
|
||||
|
||||
constructor(messageService: MessageService) {
|
||||
messageService.messageAnnounced$.subscribe(
|
||||
message=>{
|
||||
this.globalMessageOpened = true;
|
||||
this.globalMessage = message;
|
||||
console.log('received message:' + message);
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.globalMessageOpened = false;
|
||||
}
|
||||
}
|
14
harbor-app/src/app/global-message/message.service.ts
Normal file
14
harbor-app/src/app/global-message/message.service.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
|
||||
@Injectable()
|
||||
export class MessageService {
|
||||
|
||||
private messageAnnouncedSource = new Subject<string>();
|
||||
|
||||
messageAnnounced$ = this.messageAnnouncedSource.asObservable();
|
||||
|
||||
announceMessage(message: string) {
|
||||
this.messageAnnouncedSource.next(message);
|
||||
}
|
||||
}
|
@ -1,16 +1,21 @@
|
||||
<clr-modal [(clrModalOpen)]="create_project_opened">
|
||||
<clr-modal [(clrModalOpen)]="createProjectOpened">
|
||||
<h3 class="modal-title">New Project</h3>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<section class="form-block">
|
||||
<div class="form-group">
|
||||
<label for="create_project_username" class="col-md-4">Project Name</label>
|
||||
<input type="text" class="col-md-8" id="create_project_username" size="20">
|
||||
<label for="create_project_name" class="col-md-4">Project Name</label>
|
||||
<label for="create_project_name" aria-haspopup="true" role="tooltip" [class.invalid]="hasError" [class.valid]="!hasError" class="tooltip tooltip-validation tooltip-sm tooltip-bottom-right">
|
||||
<input type="text" id="create_project_name" [(ngModel)]="project.name" name="name" size="20" (keyup)="hasError=false;">
|
||||
<span class="tooltip-content">
|
||||
{{errorMessage}}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-4">Public</label>
|
||||
<div class="checkbox-inline">
|
||||
<input type="checkbox" id="create_project_public">
|
||||
<input type="checkbox" id="create_project_public" [(ngModel)]="project.public" name="public">
|
||||
<label for="create_project_public"></label>
|
||||
</div>
|
||||
</div>
|
||||
@ -18,8 +23,8 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="create_project_opened = false">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" (click)="create_project_opened = false">Ok</button>
|
||||
<button type="button" class="btn btn-outline" (click)="createProjectOpened = false">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" (click)="onSubmit()">Ok</button>
|
||||
</div>
|
||||
</clr-modal>
|
||||
<button class="btn btn-sm" (click)="create_project_opened = true">New Project</button>
|
||||
<button class="btn btn-sm" (click)="newProject()">New Project</button>
|
@ -1,9 +1,55 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, EventEmitter, Output } from '@angular/core';
|
||||
import { Response } from '@angular/http';
|
||||
|
||||
import { Project } from '../project';
|
||||
import { ProjectService } from '../project.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'create-project',
|
||||
templateUrl: 'create-project.component.html'
|
||||
templateUrl: 'create-project.component.html',
|
||||
styleUrls: [ 'create-project.css' ]
|
||||
})
|
||||
export class CreateProjectComponent {
|
||||
|
||||
project: Project = new Project();
|
||||
createProjectOpened: boolean;
|
||||
|
||||
errorMessage: string;
|
||||
hasError: boolean;
|
||||
|
||||
@Output() create = new EventEmitter<boolean>();
|
||||
|
||||
constructor(private projectService: ProjectService) {}
|
||||
|
||||
onSubmit() {
|
||||
this.hasError = false;
|
||||
this.projectService
|
||||
.createProject(this.project.name, this.project.public)
|
||||
.subscribe(
|
||||
status=>{
|
||||
this.create.emit(true);
|
||||
this.createProjectOpened = false;
|
||||
},
|
||||
error=>{
|
||||
this.hasError = true;
|
||||
if (error instanceof Response) {
|
||||
switch(error.status) {
|
||||
case 409:
|
||||
this.errorMessage = 'Project name already exists.'; break;
|
||||
case 400:
|
||||
this.errorMessage = 'Project name is illegal.'; break;
|
||||
default:
|
||||
this.errorMessage = 'Unknown error for project name.';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
newProject() {
|
||||
this.hasError = false;
|
||||
this.project = new Project();
|
||||
this.createProjectOpened = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-left'">
|
||||
<button class="btn btn-sm btn-outline-primary" clrDropdownToggle>
|
||||
My Projects
|
||||
{{currentType.value}}
|
||||
<clr-icon shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem>My Projects</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem>Public Projects</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem *ngFor="let p of types" (click)="doFilter(p.key)">{{p.value}}</a>
|
||||
</div>
|
||||
</clr-dropdown>
|
@ -1,7 +1,23 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, Output, EventEmitter } from '@angular/core';
|
||||
|
||||
export const projectTypes = [
|
||||
{ 'key' : 0, 'value': 'My Projects' },
|
||||
{ 'key' : 1, 'value': 'Public Projects'}
|
||||
];
|
||||
|
||||
@Component({
|
||||
selector: 'filter-project',
|
||||
templateUrl: 'filter-project.component.html'
|
||||
})
|
||||
export class FilterProjectComponent {}
|
||||
export class FilterProjectComponent {
|
||||
|
||||
@Output() filter = new EventEmitter<number>();
|
||||
types = projectTypes;
|
||||
currentType = projectTypes[0];
|
||||
|
||||
doFilter(type: number) {
|
||||
console.log('Filtered projects by:' + type);
|
||||
this.currentType = projectTypes.find(item=>item.key === type);
|
||||
this.filter.emit(type);
|
||||
}
|
||||
}
|
@ -8,12 +8,12 @@
|
||||
<clr-dg-column>Description</clr-dg-column>
|
||||
<clr-dg-row *ngFor="let p of projects">
|
||||
<clr-dg-cell><a [routerLink]="['/harbor', 'projects', p.id, 'repository']" >{{p.name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell>{{p.isPublic ? 'Public': 'Private'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.repoCount}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.creationTime}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.destination}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.public == 1 ? 'Public': 'Private'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.repo_count}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.creation_time}}</clr-dg-cell>
|
||||
<clr-dg-cell>--</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.owner}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.description}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>{{projects.length}} item(s)</clr-dg-footer>
|
||||
<clr-dg-footer>{{ (projects ? projects.length : 0) }} item(s)</clr-dg-footer>
|
||||
</clr-datagrid>
|
@ -1,46 +1,24 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { Project } from '../project';
|
||||
import { ProjectService } from '../project.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'list-project',
|
||||
templateUrl: 'list-project.component.html'
|
||||
})
|
||||
export class ListProjectComponent implements OnInit {
|
||||
projects: Project[];
|
||||
export class ListProjectComponent {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.projects = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Project01',
|
||||
isPublic: true,
|
||||
repoCount: 0,
|
||||
creationTime: '2016-12-29 14:58 PM',
|
||||
destination: '10.117.4.61',
|
||||
owner: 'Administrator',
|
||||
description: 'New updated by Alice.'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Project02',
|
||||
isPublic: false,
|
||||
repoCount: 2,
|
||||
creationTime: '2016-12-29 15:28 PM',
|
||||
destination: '10.117.4.61',
|
||||
owner: 'Administrator',
|
||||
description: '--'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Project03',
|
||||
isPublic: true,
|
||||
repoCount: 5,
|
||||
creationTime: '2016-12-21 10:25 AM',
|
||||
destination: '10.117.4.61',
|
||||
owner: 'Administrator',
|
||||
description: 'Deprecated'
|
||||
},
|
||||
];
|
||||
projects: Project[];
|
||||
errorMessage: string;
|
||||
|
||||
constructor(private projectService: ProjectService) {}
|
||||
|
||||
retrieve(name: string, isPublic: number): void {
|
||||
this.projectService
|
||||
.listProjects(name, isPublic)
|
||||
.subscribe(
|
||||
response => this.projects = response,
|
||||
error => this.errorMessage = <any>error);
|
||||
}
|
||||
}
|
@ -1,20 +1,11 @@
|
||||
<h3>Projects</h3>
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="col-xs-4">
|
||||
<create-project></create-project>
|
||||
<create-project (create)="createProject($event)"></create-project>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-left'">
|
||||
<button class="btn btn-sm btn-outline-primary" clrDropdownToggle>
|
||||
My Projects
|
||||
<clr-icon shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem>My Projects</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem>Public Projects</a>
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<search-project></search-project>
|
||||
<filter-project (filter)="filterProjects($event)"></filter-project>
|
||||
<search-project (search)="searchProjects($event)"></search-project>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<list-project></list-project>
|
||||
|
@ -1,14 +1,39 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Project } from './project';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ListProjectComponent } from './list-project/list-project.component';
|
||||
|
||||
@Component({
|
||||
selector: 'project',
|
||||
templateUrl: 'project.component.html',
|
||||
styleUrls: [ 'project.css' ]
|
||||
})
|
||||
export class ProjectComponent implements OnInit {
|
||||
ngOnInit(): void {
|
||||
|
||||
|
||||
@ViewChild(ListProjectComponent)
|
||||
listProjects: ListProjectComponent;
|
||||
lastFilteredType: number = 0;
|
||||
|
||||
createProject(created: boolean): void {
|
||||
console.log('Project has been created:' + created);
|
||||
this.listProjects.retrieve('', 0);
|
||||
}
|
||||
|
||||
filterProjects(type: number): void {
|
||||
this.lastFilteredType = type;
|
||||
this.listProjects.retrieve('', type);
|
||||
console.log('Projects were filtered by:' + type);
|
||||
|
||||
}
|
||||
|
||||
searchProjects(projectName: string): void {
|
||||
console.log('Search for project name:' + projectName);
|
||||
this.listProjects.retrieve(projectName, this.lastFilteredType);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.listProjects.retrieve('', 0);
|
||||
}
|
||||
|
||||
}
|
@ -14,6 +14,7 @@ import { ProjectDetailComponent } from './project-detail/project-detail.componen
|
||||
import { MemberComponent } from './member/member.component';
|
||||
import { ProjectRoutingModule } from './project-routing.module';
|
||||
|
||||
import { ProjectService } from './project.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -32,6 +33,9 @@ import { ProjectRoutingModule } from './project-routing.module';
|
||||
ProjectDetailComponent,
|
||||
MemberComponent
|
||||
],
|
||||
exports: [ ListProjectComponent ]
|
||||
exports: [ ListProjectComponent ],
|
||||
providers: [ ProjectService ]
|
||||
})
|
||||
export class ProjectModule {}
|
||||
export class ProjectModule {
|
||||
|
||||
}
|
38
harbor-app/src/app/project/project.service.ts
Normal file
38
harbor-app/src/app/project/project.service.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Http, Headers, RequestOptions } from '@angular/http';
|
||||
import { Project } from './project';
|
||||
|
||||
import { BaseService } from '../service/base.service';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
@Injectable()
|
||||
export class ProjectService extends BaseService {
|
||||
|
||||
headers = new Headers({'Content-type': 'application/json'});
|
||||
options = new RequestOptions({'headers': this.headers});
|
||||
|
||||
constructor(private http: Http) {
|
||||
super();
|
||||
}
|
||||
|
||||
listProjects(name: string, isPublic: number): Observable<Project[]>{
|
||||
return this.http
|
||||
.get(`/ng/api/projects?project_name=${name}&is_public=${isPublic}`, this.options)
|
||||
.map(response=>response.json())
|
||||
.catch(this.handleError);
|
||||
}
|
||||
|
||||
createProject(name: string, isPublic: number): Observable<any> {
|
||||
return this.http
|
||||
.post(`/ng/api/projects`,
|
||||
JSON.stringify({'project_name': name, 'public': (isPublic ? 1 : 0)})
|
||||
, this.options)
|
||||
.map(response=>response.status)
|
||||
.catch(error=>Observable.throw(error));
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,32 @@
|
||||
/*
|
||||
[
|
||||
{
|
||||
"project_id": 1,
|
||||
"owner_id": 1,
|
||||
"name": "library",
|
||||
"creation_time": "2017-02-10T07:57:56Z",
|
||||
"creation_time_str": "",
|
||||
"deleted": 0,
|
||||
"owner_name": "",
|
||||
"public": 1,
|
||||
"Togglable": true,
|
||||
"update_time": "2017-02-10T07:57:56Z",
|
||||
"current_user_role_id": 1,
|
||||
"repo_count": 0
|
||||
}
|
||||
]
|
||||
*/
|
||||
export class Project {
|
||||
id: number;
|
||||
project_id: number;
|
||||
owner_id: number;
|
||||
name: string;
|
||||
isPublic: boolean;
|
||||
repoCount: number;
|
||||
creationTime: string;
|
||||
destination: string;
|
||||
owner: string;
|
||||
description: string;
|
||||
creation_time: Date;
|
||||
creation_time_str: string;
|
||||
deleted: number;
|
||||
owner_name: string;
|
||||
public: number;
|
||||
Togglable: boolean;
|
||||
update_time: Date;
|
||||
current_user_role_id: number;
|
||||
repo_count: number;
|
||||
}
|
@ -1 +1 @@
|
||||
<input type="text" placeholder="Search for projects">
|
||||
<input type="text" placeholder="Search for projects" #searchProject (keyup.enter)="doSearch(searchProject.value)" >
|
@ -1,9 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, EventEmitter, Output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'search-project',
|
||||
templateUrl: 'search-project.component.html'
|
||||
})
|
||||
export class SearchProjectComponent {
|
||||
@Output() search = new EventEmitter<string>();
|
||||
|
||||
doSearch(projectName) {
|
||||
this.search.emit(projectName);
|
||||
}
|
||||
}
|
18
harbor-app/src/app/service/base.service.ts
Normal file
18
harbor-app/src/app/service/base.service.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Http, Response} from '@angular/http';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
export abstract class BaseService {
|
||||
protected handleError(error: Response | any) {
|
||||
// In a real world app, we might use a remote logging infrastructure
|
||||
let errMsg: string;
|
||||
if (error instanceof Response) {
|
||||
const body = error.json() || '';
|
||||
const err = body.error || JSON.stringify(body);
|
||||
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
|
||||
} else {
|
||||
errMsg = error.message ? error.message : error.toString();
|
||||
}
|
||||
console.error(errMsg);
|
||||
return Observable.throw(errMsg);
|
||||
}
|
||||
}
|
@ -2,15 +2,21 @@ import { NgModule } from '@angular/core';
|
||||
import { CoreModule } from '../core/core.module';
|
||||
|
||||
import { SessionService } from '../shared/session.service';
|
||||
import { MessageComponent } from '../global-message/message.component';
|
||||
import { MessageService } from '../global-message/message.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CoreModule
|
||||
],
|
||||
exports: [
|
||||
CoreModule
|
||||
declarations: [
|
||||
MessageComponent
|
||||
],
|
||||
providers: [SessionService]
|
||||
exports: [
|
||||
CoreModule,
|
||||
MessageComponent
|
||||
],
|
||||
providers: [SessionService, MessageService]
|
||||
})
|
||||
export class SharedModule {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user