You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@griffin.apache.org by gu...@apache.org on 2017/10/11 04:31:12 UTC
[12/13] incubator-griffin git commit: refactor ui with angular2
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/job/create-job/create-job.component.html
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/job/create-job/create-job.component.html b/ui/angular/src/app/job/create-job/create-job.component.html
new file mode 100644
index 0000000..23e62d0
--- /dev/null
+++ b/ui/angular/src/app/job/create-job/create-job.component.html
@@ -0,0 +1,252 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+-->
+<div class="mask" *ngIf = 'maskOpen' (click)="close()"></div>
+
+<div class="container-fluid">
+ <div class="row">
+ <h5 class="over-title margin-bottom-15 job">Create Job</h5>
+ </div><!--//row-->
+ <div class="row">
+ <!-- <form name="Form" id="form" novalidate> -->
+ <form name= "Form" id="form" (ngSubmit)="submit(jobForm)" #jobForm="ngForm" novalidate>
+ <div class="formStep" >
+ <label class="stepDesc info">Please setup the job required information</label>
+ <div class="container-fluid">
+
+ <!-- schema definition list -->
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <fieldset (window:resize)="onResize($event)">
+ <legend>
+ Required Information
+ </legend>
+ <div class="y-scrollable">
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group"
+ [ngClass]="{'has-error':jobName.dirty&&jobName.invalid, 'has-success':jobName.valid}">
+ <label class="col-md-2 col-lg-2 col-sm-2 control-label">
+ Source Pattern<span class="symbol required"></span>:
+ </label>
+
+ <div class="col-md-10 col-lg-10 col-sm-10 ">
+ <input type="text" class="form-control" [(ngModel)]="sourcePat" #jobName="ngModel" name="jobName" placeholder="Please input the source partition, such as 'YYYYMMdd-HH'." required
+ pattern="YYYYMMdd-HH"
+ maxlength='11'>
+ <!-- /i<span class="error text-small block " *ngIf="jobName.dirty && jobName.errors.Pattern">Please input partition like 'YYYYMMdd-HH'</span> jobName.dirty && jobName.errors.pattern-->
+ <span class="error text-small block " *ngIf="jobName.dirty&&jobName.invalid">Please input partition like 'YYYYMMdd-HH'</span>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group" [ngClass]="{'has-error':targetName.dirty&&targetName.invalid, 'has-success':targetName.valid}">
+ <label class="col-md-2 col-lg-2 col-sm-2 control-label">
+ Target Pattern<span class="symbol required"></span>:
+ </label>
+ <div class="col-md-10 col-lg-10 col-sm-10 ">
+ <input type="text" class="form-control" [(ngModel)]="targetPat" placeholder="Please input target partition of your job, such as 'YYYYMMdd-HH'."
+ required pattern="YYYYMMdd-HH" maxlength='11' name="targetName" #targetName="ngModel">
+ <span class="error text-small block " *ngIf="targetName.dirty&&targetName.invalid">Please input partition like 'YYYYMMdd-HH'</span>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group">
+ <label for="measureSelector" class="col-md-2 col-lg-2 col-sm-2 control-label">
+ Measure Name:
+ </label>
+
+ <div class="col-md-10 col-lg-10 col-sm-10 ">
+ <select id="measureSelector" class="form-control"
+ ngControl="name" required name="measure.name" [(ngModel)]="measure">
+ <option *ngFor="let row of Measures" value="{{row.name}}" >{{row.name}}</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group" >
+ <label class="col-md-2 col-lg-2 col-sm-2 control-label">
+ Start At:
+ </label>
+
+ <!-- <div class="col-md-5 col-lg-5 col-sm-5 ">
+ <input type="text" id="datepicker" class="form-control" name="jobStartTime" [(ngModel)]="jobStartTime" >
+ </div> -->
+ <div class="col-md-5 col-lg-5 col-sm-5 ">
+ <!-- <md-form-field> -->
+ <input matInput [matDatepicker]="picker" placeholder="Choose a date" style="color:black;" [(ngModel)]="jobStartTime" name="jobStartTime"
+ >
+ <mat-datepicker-toggle mdSuffix [for]="picker" (click)="setHeight()"></mat-datepicker-toggle>
+ <mat-datepicker #picker ></mat-datepicker>
+
+
+ <!-- </md-form-field> -->
+ </div>
+ <div class="col-md-5 col-lg-5 col-sm-5" [ngClass]="{'has-error':timeDetail.invalid, 'has-success':timeDetail.valid}">
+ <input type="text" class="form-control" id="timeDetail" (click)="showTime()" value="{{hourDetail}}:{{minuteDetail}}:{{secondDetail}} "
+ [(ngModel)]="timeDetail" name="time"
+
+ >
+ <div id="timePopup" class="col-md-11 col-lg-11 col-sm-11 setgrey" *ngIf="isOpen">
+ <div id="hourSelector">
+ <p (click)="changeTime(0,23,true,hourDetail,1)"><i class="fa fa-caret-up" aria-hidden="true"></i></p>
+ <p>{{hourDetail}}</p>
+ <p (click)="changeTime(0,23,false,hourDetail,1)"><i class="fa fa-caret-down" aria-hidden="true"></i></p>
+ </div>
+ <div class="division">
+ <p>:</p>
+ </div>
+ <div id="minuteSelector">
+ <p (click)="changeTime(0,59,true,minuteDetail,2)"><i class="fa fa-caret-up" aria-hidden="true"></i></p>
+ <p >{{minuteDetail}}</p>
+ <p (click)="changeTime(0,59,false,minuteDetail,2)"><i class="fa fa-caret-down" aria-hidden="true"></i></p>
+ </div>
+ <div class="division">
+ <p>:</p>
+ </div>
+ <div id="secondSelector">
+ <p (click)="changeTime(0,59,true,secondDetail,3)"><i class="fa fa-caret-up" aria-hidden="true"></i></p>
+ <p >{{secondDetail}}</p>
+ <p (click)="changeTime(0,59,false,secondDetail,3)"><i class="fa fa-caret-down" aria-hidden="true"></i></p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group">
+ <label for="systemSelector" class="col-md-2 col-lg-2 col-sm-2 control-label">
+ Interval:
+ </label>
+
+ <div class="col-md-5 col-lg-5 col-sm-5 ">
+ <input type="text" id="systemSelector" class="form-control" [(ngModel)]="periodTime" required placeholder="How often it works" name="periodTime"
+ onblur="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.replace(/\D/g,'')}"
+ onafterpaste="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.replace(/\D/g,'')}">
+ </div>
+ <div class="col-md-5 col-lg-5 col-sm-5 ">
+ <select id="timeSelector" class="form-control" [(ngModel)]="timeType" name="timeSelector" required>
+ <!--<option value="hours" >hours</option>-->
+ <!--<option value="minutes" >minutes</option>-->
+ <!--<option value="seconds" >seconds</option>-->
+ <option *ngFor="let time of Times" >{{time}}</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="setcolor">
+ <p>
+ <i class="fa fa-info-circle"></i> After submitted, please go to "
+ <a class="bark-link" href="/jobs">Jobs</a>" to check the job status
+ </p>
+ </div>
+
+ </fieldset>
+ </div>
+
+ <div class="form-group btn-container" >
+ <button class="btn btn-primary btn-o back-step btn-wide pull-left" (click)="form.prev()">
+ <i class="fa fa-arrow-circle-left"></i> Back
+ </button>
+ <!-- <button class="btn btn-primary btn-o next-step btn-wide pull-right" (click)="form.submit(Form)"> -->
+ <toaster-container></toaster-container>
+ <button type="submit" class="btn btn-primary btn-o next-step btn-wide pull-right" (click)="submit(jobForm)"
+ >
+ <!-- <button class="btn btn-primary btn-o next-step btn-wide pull-right" data-toggle="modal" data-target="#confirm"> -->
+ Submit
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <div class="modal fade" id="confirm-job" role="dialog" [ngClass]="{'in': visibleAnimate}"
+ [ngStyle]="{'display': visible ? 'block' : 'none', 'opacity': visibleAnimate ? 1 : 0}"
+ (click)="onContainerClicked($event)">
+ <div class="modal-dialog modal-xg modal-lg">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true" (click)="hide()">×</button>
+ <h4 class="modal-title">Save the job with the below information?</h4>
+ </div>
+ <div class="modal-body">
+ <!-- <ng-include src="'/pages/jobs/confirmation-ac.html'"/> -->
+ <div class="container-fluid" id="viewJobContent" style="overflow:auto;">
+ <div class="row">
+ <h5 class="over-title margin-bottom-15">Basic information</h5>
+ </div><!--//row-->
+ <div class="row">
+ <div class="col-lg-12 col-md-12 col-sm-12">
+ <div id="viewrule-definition" class="viewrule-content">
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Source Pattern:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff">
+ {{sourcePat}}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Target Pattern:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff">
+ {{targetPat}}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Measure Name:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff">
+ {{measure}}
+ <!-- {{Measures}} -->
+ </div>
+ </div>
+ <div class="row">
+ <label for="systemSelector" class="col-md-4 col-lg-4 col-sm-4">
+ Start at:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff" *ngIf="jobStartTime">
+ {{jobStartTime.toLocaleDateString("en-US")}} {{timeDetail}}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Interval:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8" style="color: #fff">{{periodTime}} {{timeType}}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!--//row-->
+ <br/>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal" (click)="hide()">Cancel</button>
+ <button type="button" class="btn btn-primary" (click)="save()">Save</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </form>
+ </div><!--//row-->
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/job/create-job/create-job.component.spec.ts
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/job/create-job/create-job.component.spec.ts b/ui/angular/src/app/job/create-job/create-job.component.spec.ts
new file mode 100644
index 0000000..44292d5
--- /dev/null
+++ b/ui/angular/src/app/job/create-job/create-job.component.spec.ts
@@ -0,0 +1,43 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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 { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CreateJobComponent } from './create-job.component';
+
+describe('CreateJobComponent', () => {
+ let component: CreateJobComponent;
+ let fixture: ComponentFixture<CreateJobComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ CreateJobComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CreateJobComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should be created', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/job/create-job/create-job.component.ts
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/job/create-job/create-job.component.ts b/ui/angular/src/app/job/create-job/create-job.component.ts
new file mode 100644
index 0000000..1cadc85
--- /dev/null
+++ b/ui/angular/src/app/job/create-job/create-job.component.ts
@@ -0,0 +1,229 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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 { Component, OnInit } from '@angular/core';
+import { FormControl } from '@angular/forms';
+import { FormsModule } from '@angular/forms';
+import { MaxLengthValidator } from '@angular/forms';
+import { NgControlStatus ,Validators} from '@angular/forms';
+import { PatternValidator } from '@angular/forms';
+// import {MdDatepickerModule} from '@angular/material';
+import {MatDatepickerModule} from '@angular/material';
+import {ServiceService} from '../../service/service.service';
+
+
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {ToasterModule, ToasterService, ToasterConfig} from 'angular2-toaster';
+import * as $ from 'jquery';
+import {HttpClient,HttpParams} from '@angular/common/http';
+import {Router} from "@angular/router";
+
+@Component({
+ selector: 'app-create-job',
+ templateUrl: './create-job.component.html',
+ providers:[ServiceService],
+ styleUrls: ['./create-job.component.css']
+})
+export class CreateJobComponent implements OnInit {
+
+ constructor(toasterService: ToasterService,private http: HttpClient,private router:Router,public servicecService:ServiceService) {
+ this.toasterService = toasterService;
+ };
+
+ public toasterconfig : ToasterConfig =
+ new ToasterConfig({
+ showCloseButton: true,
+ tapToDismiss: false,
+ timeout: 0
+ });
+
+ currentStep = 1;
+ Times = ['seconds','minutes','hours'];
+ timeType = 'seconds';
+ isOpen = false;
+ maskOpen = false;
+
+ hourDetail = '00';
+ minuteDetail = '00';
+ secondDetail = '00';
+ timeDetail = '00:00:00';
+ periodTime :number;
+ StartTime = '';
+ sourcePat :'';
+ targetPat :'';
+ createResult = '';
+ jobStartTime : any;
+
+ Measures:object;
+
+ measure:string;
+ measureid:string;
+ ntAccount = 0;
+ newJob={
+ "sourcePattern":'',
+ "targetPattern":'',
+ "jobStartTime":0,
+ "interval":'',
+ "groupName":'',
+ }
+
+ private toasterService: ToasterService;
+
+
+ public visible = false;
+ public visibleAnimate = false;
+
+ public hide(): void {
+ this.visibleAnimate = false;
+ setTimeout(() => this.visible = false, 300);
+ }
+
+ public onContainerClicked(event: MouseEvent): void {
+ if ((<HTMLElement>event.target).classList.contains('modal')) {
+ this.hide();
+ }
+ }
+
+ changeTime(min,max,increase,time,type){
+ time = parseInt(time);
+ if(increase){
+ if(time==max)
+ time = min;
+ else time = time + 1;
+ }
+ else{
+ if(time==min)
+ time = max;
+ else time = time - 1;
+ }
+ if(time < 10)
+ time = '0' + time;
+ if(type==1)
+ this.hourDetail = time;
+ else if(type==2)
+ this.minuteDetail = time;
+ else
+ this.secondDetail = time;
+ this.timeDetail = this.hourDetail+':'+this.minuteDetail+':'+this.secondDetail;
+ }
+
+ showTime(){
+ this.isOpen = !this.isOpen;
+ this.maskOpen = !this.maskOpen;
+ }
+
+ close(){
+ this.isOpen = false;
+ this.maskOpen = false;
+ }
+
+ prev(form){
+ history.back();
+ }
+
+ submit (jobForm) {
+ // jobForm.markAsPristine();
+ var period;
+ if(this.timeType=='minutes')
+ period = this.periodTime *60;
+ else if(this.timeType=='hours')
+ period = this.periodTime * 3600;
+ else period = this.periodTime;
+ var rule = '';
+ var time :number;
+ if(this.jobStartTime){
+ var year = this.jobStartTime.getFullYear();
+ var month = this.jobStartTime.getMonth() + 1;
+ var day = this.jobStartTime.getDate();
+ var startTime = year +'-'+ month + '-'+ day + ' '+ this.timeDetail;
+ }
+
+ time = Date.parse(startTime);
+ if(isNaN(time)){
+ this.toasterService.pop('error','Error!','Please input the right format of start time');
+ return false;
+ }
+ if (!jobForm.valid) {
+ this.toasterService.pop('error', 'Error!', 'Please complete the form!');
+ return false;
+ }
+
+ this.newJob={
+ "sourcePattern":this.sourcePat,
+ "targetPattern":this.targetPat,
+ "jobStartTime":time,
+ "interval":period,
+ "groupName":'BA',
+ },
+ this.visible = true;
+ setTimeout(() => this.visibleAnimate = true, 100);
+ }
+ save() {
+ var date = new Date();
+ var datastr = date.toString();
+ var month = date.getMonth()+1;
+ var timestamp = Date.parse(datastr);
+ var jobName = this.measure + '-BA-' + this.ntAccount + '-' + timestamp;
+ var addJobs = this.servicecService.config.uri.addJobs;
+ var newJob = addJobs + '?group=' + this.newJob.groupName + '&jobName=' + jobName + '&measureId=' + this.measureid;
+ this.http
+ .post(newJob, this.newJob)
+ .subscribe(data => {
+ this.createResult = data['results'];
+ this.hide();
+ this.router.navigate(['/jobs']);
+ },
+ err => {
+ console.log('Error when creating job');
+ });
+ }
+
+
+ onResize(event){
+ this.resizeWindow();
+ }
+
+ resizeWindow(){
+ var stepSelection = '.formStep';
+ $(stepSelection).css({
+ height: window.innerHeight - $(stepSelection).offset().top - $('#footerwrap').outerHeight()
+ });
+ $('fieldset').height($(stepSelection).height() - $(stepSelection + '>.stepDesc').height() - $('.btn-container').height() - 80);
+ $('.y-scrollable').css({
+ 'max-height': $('fieldset').height()- $('.add-dataset').outerHeight()
+ });
+ $('#data-asset-pie').css({
+ height: $('#data-asset-pie').parent().width(),
+ width: $('#data-asset-pie').parent().width()
+ });
+ }
+
+ setHeight(){
+ $('#md-datepicker-0').height(250);
+ }
+
+ ngOnInit() {
+ var allModels = this.servicecService.config.uri.allModels;
+ this.http.get(allModels).subscribe(data =>{
+ this.Measures = data;
+ this.measure = this.Measures[0].name;
+ this.measureid = this.Measures[0].id;
+ });
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/job/job.component.css
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/job/job.component.css b/ui/angular/src/app/job/job.component.css
new file mode 100644
index 0000000..1c68dfc
--- /dev/null
+++ b/ui/angular/src/app/job/job.component.css
@@ -0,0 +1,29 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+*/
+.table-striped > tbody > tr{
+ background-color: #1f1f1f;
+}
+a{
+ color: white;
+}
+
+#pagination .pagination{
+ margin:20px 0 0 0 ;
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/job/job.component.html
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/job/job.component.html b/ui/angular/src/app/job/job.component.html
new file mode 100644
index 0000000..e2bb2de
--- /dev/null
+++ b/ui/angular/src/app/job/job.component.html
@@ -0,0 +1,192 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+-->
+
+<div >
+ <p>
+ <a routerLink="/createjob" class="btn btn-primary btn-o btn-wide" >
+ <i class="fa fa-plus"></i> Create Job</a>
+ </p>
+
+ <div id="modelContainer">
+ <table class="table table-striped" [mfData]="results" #mf="mfDataTable" [mfRowsOnPage]="10">
+ <thead>
+ <tr style="background-color:#7D95CC">
+ <th st-ratio="15">Job Name</th>
+ <th st-ratio="15">Source Pattern</th>
+ <th st-ratio="15">Target Pattern</th>
+ <th st-ratio="15">Previous Fire Time</th>
+
+ <th st-ratio="20">Next Fire Time</th>
+ <th st-ratio="15">Trigger State</th>
+
+ <th st-ratio="5">Interval</th>
+ <th st-ratio="5">Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngIf="!results">
+ <td colspan="7" style="text-align:center">No content!</td>
+ </tr>
+ </tbody>
+ <tbody *ngFor="let row of mf.data">
+ <tr (click) = "showInstances(row)">
+ <td [ngClass]="{accordion:true}" style="cursor: pointer;">
+ <i *ngIf="!row.showDetail" class="fa fa-chevron-circle-right blue"></i>
+ <i *ngIf="row.showDetail" class="fa fa-chevron-circle-down blue"></i>
+ {{row.jobName}}
+ -{{(row.createTime | date: 'yyyy/MM/dd HH:mm:ss') || 'N/A' }}
+ </td>
+ <td>{{row.sourcePattern}}</td>
+ <td>{{row.targetPattern}}</td>
+ <td [hidden]="row.previousFireTime!=-1">--/--/-- --:--</td>
+ <td [hidden]="row.previousFireTime==-1">{{(row.previousFireTime | date: 'yyyy/MM/dd HH:mm:ss') || 'N/A' }}</td>
+ <td>{{(row.nextFireTime | date: 'yyyy/MM/dd HH:mm:ss') || 'N/A' }}</td>
+ <td>
+ <span *ngIf='row.triggerState == "NORMAL"' class="normal">{{row.triggerState}}
+ </span>
+ <span *ngIf='row.triggerState != "NORMAL"' class = 'unnormal'>{{row.triggerState}}</span>
+ </td>
+ <td>{{row.interval}}</td>
+ <td>
+
+ <a (click)="remove(row)" title="delete" style="text-decoration:none">
+ <i class="fa fa-trash-o"></i>
+ </a>
+
+ </td>
+ </tr>
+ <tr *ngIf="row.showDetail">
+ <td colspan="7" style="padding:20px 30px 10px 30px;">
+ <table class="table table-striped" [mfData]="allInstances" #mf2="mfDataTable" [mfRowsOnPage]="10">
+ <thead>
+ <tr style="background-color:#7D95CC">
+ <th style="width:30%" >AppID</th>
+ <th style="width:25%" >Time</th>
+ <th style="width:20%" >State</th>
+ </tr>
+ </thead>
+ <tbody >
+ <tr *ngIf="!allInstances">
+ <td colspan="7" style="text-align:center">No content.</td>
+ </tr>
+ <tr *ngFor="let item of mf2.data" >
+ <td><a href={{item.appUri}} target="_blank" style="color:white">{{item.appId}}</a></td>
+ <td>{{item.timestamp | date: 'yyyy/MM/dd HH:mm:ss'}}</td>
+ <td>{{item.state}}</td>
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <td class="text-center" colspan="8" style="background-color:#1f1f1f;" id="pagination">
+ <mfBootstrapPaginator></mfBootstrapPaginator>
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <td colspan="8" class="text-right" >
+ <mfBootstrapPaginator></mfBootstrapPaginator>
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+ </div>
+ <div class="modal fade" id="deleteJobConfirmation" role="dialog" tabindex="-1" [ngClass]="{'in': visibleAnimate}" *ngIf="deletedRow"
+ [ngStyle]="{'display': visible ? 'block' : 'none', 'opacity': visibleAnimate ? 1 : 0}"
+ (click)="onContainerClicked($event)">
+ <div class="modal-dialog modal-xg modal-lg">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title">Delete the job with the below information?</h4>
+ </div>
+ <div class="modal-body">
+ <!-- <ng-include src="'/pages/jobs/delete-confirm.html'"/> -->
+ <div class="container-fluid" id="deleteContent" style="overflow:auto;">
+ <div class="row">
+ <h5 class="over-title margin-bottom-15">Basic information</h5>
+ </div><!--//row-->
+ <div class="row">
+ <div class="col-lg-12 col-md-12 col-sm-12">
+ <div id="viewrule-definition" class="viewrule-content">
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Job Name:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff">
+ {{deletedRow.jobName}}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Source Pattern:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff">
+ {{deletedRow.sourcePattern}}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Target Pattern:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff">
+ {{deletedRow.targetPattern}}
+ </div>
+ </div>
+ <div class="row">
+ <label for="systemSelector" class="col-md-4 col-lg-4 col-sm-4">
+ Next Fire Time:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff">
+ {{(deletedRow.nextFireTime | date: 'yyyy/MM/dd HH:mm:ss') || 'N/A' }}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Interval:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8" style="color: #fff">
+ {{deletedRow.interval}}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Group:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8" style="color: #fff">
+ {{deletedRow.groupName}}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!--//row-->
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-primary" (click)="confirmDelete()">Yes</button>
+ <button type="button" class="btn btn-default" data-dismiss="modal" (click)="hide()">No</button>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/job/job.component.spec.ts
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/job/job.component.spec.ts b/ui/angular/src/app/job/job.component.spec.ts
new file mode 100644
index 0000000..2caf041
--- /dev/null
+++ b/ui/angular/src/app/job/job.component.spec.ts
@@ -0,0 +1,43 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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 { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { JobComponent } from './job.component';
+
+describe('JobComponent', () => {
+ let component: JobComponent;
+ let fixture: ComponentFixture<JobComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ JobComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(JobComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should be created', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/job/job.component.ts
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/job/job.component.ts b/ui/angular/src/app/job/job.component.ts
new file mode 100644
index 0000000..5e4a2ea
--- /dev/null
+++ b/ui/angular/src/app/job/job.component.ts
@@ -0,0 +1,165 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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 { Component, OnInit } from '@angular/core';
+import { HttpClient} from '@angular/common/http';
+import { Ng2SmartTableModule ,LocalDataSource} from 'ng2-smart-table';
+import {DataTableModule} from "angular2-datatable";
+import {ServiceService} from '../service/service.service';
+
+import { DatePipe } from '@angular/common';
+import { Router} from "@angular/router";
+import * as $ from 'jquery';
+
+@Component({
+ selector: 'app-job',
+ templateUrl: './job.component.html',
+ providers:[ServiceService],
+ styleUrls: ['./job.component.css']
+})
+export class JobComponent implements OnInit {
+ // results:object[];
+ allInstances:any;
+ results:any;
+ source:LocalDataSource;
+ deletedBriefRow:object;
+ jobName:string;
+ public visible = false;
+ public visibleAnimate = false;
+ oldindex:number;
+
+
+ deletedRow : object;
+ sourceTable :string;
+ targetTable :string;
+ deleteId : string;
+ deleteIndex:number;
+ deleteGroup :string;
+ deleteJob :string;
+
+
+
+ constructor(private http:HttpClient,private router:Router,public servicecService:ServiceService) { };
+
+ public hide(): void {
+ this.visibleAnimate = false;
+ setTimeout(() => this.visible = false, 300);
+ }
+
+ public onContainerClicked(event: MouseEvent): void {
+ if ((<HTMLElement>event.target).classList.contains('modal')) {
+ this.hide();
+ }
+ }
+
+ // resultData = [{"jobName":"i-BA-0-1504837194000","measureId":"22","groupName":"BA","targetPattern":"YYYYMMdd-HH","triggerState":"NORMAL","nextFireTime":1505875500000,"previousFireTime":1504864200000,"interval":"300","sourcePattern":"YYYYMMdd-HH","jobStartTime":"1504800000000"},{"jobName":"i-BA-0-1504837194000","measureId":"22","groupName":"BA","targetPattern":"YYYYMMdd-HH","triggerState":"NORMAL","nextFireTime":1505875500000,"previousFireTime":1504864200000,"interval":"300","sourcePattern":"YYYYMMdd-HH","jobStartTime":"1504800000000"},{"jobName":"i-BA-0-1504837194000","measureId":"22","groupName":"BA","targetPattern":"YYYYMMdd-HH","triggerState":"NORMAL","nextFireTime":1505875500000,"previousFireTime":1504864200000,"interval":"300","sourcePattern":"YYYYMMdd-HH","jobStartTime":"1504800000000"}];
+ remove(row){
+ this.visible = true;
+ setTimeout(() => this.visibleAnimate = true, 100);
+ this.deletedRow = row;
+ this.deleteIndex = this.results.indexOf(row);
+ this.deletedBriefRow = row;
+ this.deleteGroup = row.groupName;
+ this.deleteJob = row.jobName;
+ }
+
+ confirmDelete(){
+ let deleteJob = this.servicecService.config.uri.deleteJob;
+ let deleteUrl = deleteJob + '?group=' + this.deleteGroup + '&jobName=' + this.deleteJob;
+ this.http.delete(deleteUrl).subscribe(data => {
+ let deleteResult:any = data;
+ console.log(deleteResult.code);
+ if(deleteResult.code==206){
+ var self = this;
+ self.hide();
+ setTimeout(function () {
+ self.results.splice(self.deleteIndex,1);
+ self.source.load(self.results);
+ },0);
+ }
+ },
+ err =>{
+ console.log('Error when deleting record');
+
+ });
+ };
+
+ showInstances(row){
+ if(row.showDetail){
+ row.showDetail = !row.showDetail;
+ return;
+ }
+ let index = this.results.indexOf(row);
+ if (this.oldindex!=undefined &&this.oldindex != index){
+ this.results[this.oldindex].showDetail = false;}
+ let getInstances = this.servicecService.config.uri.getInstances;
+ let getInstanceUrl = getInstances+ '?group=' + 'BA' + '&jobName=' + row.jobName +'&page='+'0'+'&size='+'200';
+ this.http.get(getInstanceUrl).subscribe(data =>{
+ row.showDetail = !row.showDetail;
+ this.allInstances = data;
+ setTimeout(function(){
+ console.log($('.pagination'));
+ $('.pagination').css("marginBottom","-10px");
+ },0);
+
+ // this.source = new LocalDataSource(this.allInstances);
+ // this.source.load(this.allInstances);
+ });
+ this.oldindex = index;
+ }
+
+ intervalFormat(second){
+ if(second<60)
+ return (second + 's');
+ else if(second<3600)
+ {
+ if(second%60==0)
+ return(second / 60 + 'min');
+ else
+ return((second - second % 60) / 60 + 'min'+second % 60 + 's');
+ }
+ else
+ {
+ if(second%3600==0)
+ return ( second / 3600 + 'h');
+ else
+ {
+ second = (second - second % 3600) / 3600 + 'h';
+ var s = second % 3600;
+ return ( second + (s-s%60)/60+'min'+s%60+'s');
+ }
+ }
+ }
+
+
+ ngOnInit():void {
+
+ var self = this;
+ let allJobs = this.servicecService.config.uri.allJobs;
+ this.http.get(allJobs).subscribe(data =>{
+ this.results = Object.keys(data).map(function(index){
+ let job = data[index];
+ job.showDetail = false;
+ job.interval = self.intervalFormat(job.interval);
+ return job;
+ });
+ });
+ // this.results = this.resultData;
+
+ };
+}
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/login/login.component.css
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/login/login.component.css b/ui/angular/src/app/login/login.component.css
new file mode 100644
index 0000000..3ad96c3
--- /dev/null
+++ b/ui/angular/src/app/login/login.component.css
@@ -0,0 +1,83 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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 url('../../../node_modules/bootstrap/dist/css/bootstrap.css');
+#content {
+ background-color: #1A237E;
+ background-position: center center;
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+ background-size: cover;
+ height: 100vh;
+}
+hr {
+ margin-bottom: 30px;
+}
+
+@media (min-width: 992px) {
+
+ #content-row {
+ margin-top:12em;
+ margin-bottom:7em;
+ }
+
+ #bark-description {
+ display: block;
+ }
+
+ #bark-description-2 {
+ display: none;
+ }
+}
+
+@media (max-width:991px) {
+
+ #content-row {
+ margin-top:0em;
+ margin-bottom:0em;
+ }
+
+ #bark-description {
+ display: none;
+ }
+
+ #bark-description-2 {
+ margin-top: 3em;
+ display: block;
+ }
+}
+
+#bark-description p, #bark-description-2 p {
+ margin-left: 100px;
+ color: #ffffff;
+ font-size: 20px;
+}
+
+#content-row {
+ padding: 3em 0;
+ background-color: rgba(255, 255, 255, 0.2);
+}
+
+#loginMsg {
+ display: none;
+ background-color: #F1D7D7;
+ color: #A95252;
+ padding: 8px 12px;
+ border-radius: 4px;
+ text-align:center;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/login/login.component.html
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/login/login.component.html b/ui/angular/src/app/login/login.component.html
new file mode 100644
index 0000000..c150ca0
--- /dev/null
+++ b/ui/angular/src/app/login/login.component.html
@@ -0,0 +1,88 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+-->
+<div *ngIf="!ntAccount" id="content" class="container-fluid">
+ <div class="row" id="content-row">
+
+ <div class="col-md-6 col-md-offset-1 col-xs-12">
+ <div id="bark-description">
+ <h3>
+ Data Quality Service Platform on eBay Cloud.
+ </h3><br>
+ <p>
+ Automates your data quality validation
+ </p><br>
+ <p>
+ Health monitoring, Profiling and detection
+ </p><br>
+ <p>
+ Unified Visualization
+ </p><br>
+ <p>
+ One set of tools to build data quality pipelines
+ </p>
+ </div>
+ </div>
+
+ <div class="col-md-3 col-md-offset-1 col-xs-12">
+ <div id="login-form">
+ <div style="text-align:center;margin-bottom:30px;">
+ <img src="../assets/img/logo.png" class="img-rounded" style="width:80%;">
+ </div>
+
+ <input type="input" class="form-control" placeholder="username" autocomplete="on" style="margin-bottom:20px;" (focus)="focus($event)">
+
+ <input type="password" class="form-control" placeholder="password" autocomplete="on" (keyup)="submit($event)" (focus)="focus($event)">
+
+ <div class="checkbox">
+ <label style="color:white;">
+ <input type="checkbox" value="remember-me" checked>Remember me
+ </label>
+ </div>
+
+ <button class="btn btn-default btn-large btn-block" id="login-btn" (click)="login()" style="margin-bottom: 20px;">Log in</button>
+
+ <div id="loginMsg">Login failed. Try again.</div>
+ </div>
+ </div>
+
+ <div class="col-xs-12">
+ <div id="bark-description-2">
+ <h3>
+ Data Quality Service Platform on the eBay Cloud.
+ </h3><br>
+ <p>
+ Automates your data quality validation
+ </p><br>
+ <p>
+ Health monitoring, Profiling and detection
+ </p><br>
+ <p>
+ Unified Visualization
+ </p><br>
+ <p>
+ One set of tools to build data quality pipelines
+ </p>
+ </div>
+ </div>
+ </div>
+
+</div>
+
+
+
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/login/login.component.spec.ts
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/login/login.component.spec.ts b/ui/angular/src/app/login/login.component.spec.ts
new file mode 100644
index 0000000..db3a317
--- /dev/null
+++ b/ui/angular/src/app/login/login.component.spec.ts
@@ -0,0 +1,43 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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 { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LoginComponent } from './login.component';
+
+describe('LoginComponent', () => {
+ let component: LoginComponent;
+ let fixture: ComponentFixture<LoginComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ LoginComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LoginComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should be created', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/login/login.component.ts
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/login/login.component.ts b/ui/angular/src/app/login/login.component.ts
new file mode 100644
index 0000000..632a18f
--- /dev/null
+++ b/ui/angular/src/app/login/login.component.ts
@@ -0,0 +1,31 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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 { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-login',
+ templateUrl: './login.component.html',
+ styleUrls: ['./login.component.css']
+ // providers:[ServiceService]
+})
+export class LoginComponent implements OnInit {
+ ngOnInit(){
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/measure/create-measure/ac/ac.component.css
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/measure/create-measure/ac/ac.component.css b/ui/angular/src/app/measure/create-measure/ac/ac.component.css
new file mode 100644
index 0000000..38ec745
--- /dev/null
+++ b/ui/angular/src/app/measure/create-measure/ac/ac.component.css
@@ -0,0 +1,119 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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 url('../../../../../node_modules/angular2-toaster/toaster.css');
+@import url('../../measure.component.css');
+
+div.tree div.tree-children::before,
+div.tree::before {
+ content: "";
+ position: absolute;
+ border-left: 1px dotted #23527c;
+ height: 100%;
+ top: -14px;
+ left: 12px
+}
+
+div.tree {
+ padding-left: 0;
+ margin-left: -5px
+}
+tree-root{
+ color: #999;
+}
+
+div.tree div.tree-children {
+ position: relative;
+ padding-left: 0;
+ margin-left: 16px
+}
+
+div.tree div.tree-children::before {
+ left: 5px
+}
+
+div.tree treenode>div>.node-wrapper {
+ margin-left: 24px
+}
+
+div.tree treenode>div>.node-wrapper>.node-content-wrapper {
+ margin-left: 4px
+}
+
+div.tree treenode>div.tree-node-leaf>.node-wrapper {
+ margin-left: 0
+}
+
+div.tree treenode>div::before {
+ content: "";
+ position: absolute;
+ border-bottom: 1px dotted #23527c;
+ width: 7px;
+ margin-top: 12px;
+ left: 7px
+}
+
+div.tree treenode>div .toggle-children-wrapper {
+ width: 13px;
+ height: 13px;
+ border: 1px solid #23527c;
+ position: absolute;
+ left: 15px;
+ margin-top: 5px;
+ margin-left: 0;
+ display: inline-block;
+ background-color: #fff;
+ z-index: 1
+}
+
+div.tree treenode>div .toggle-children-wrapper::before {
+ content: "";
+ display: inline-block;
+ width: 7px;
+ border-top: 1px solid #23527c;
+ position: absolute;
+ top: 5px;
+ left: 2px
+}
+
+div.tree treenode>div .toggle-children-wrapper.toggle-children-wrapper-collapsed::after {
+ content: "";
+ display: inline-block;
+ height: 7px;
+ border-left: 1px solid #23527c;
+ position: absolute;
+ top: 2px;
+ left: 5px
+}
+
+div.tree treenode>div .toggle-children-wrapper .toggle-children {
+ display: none
+}
+
+div.tree treenode>div .node-content-wrapper {
+ margin-left: 4px
+}
+
+div.tree>treenode>div::before {
+ left: 14px
+}
+
+div.tree>treenode>div>.node-wrapper>treenodeexpander>.toggle-children-wrapper {
+ left: 22px
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/measure/create-measure/ac/ac.component.html
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/measure/create-measure/ac/ac.component.html b/ui/angular/src/app/measure/create-measure/ac/ac.component.html
new file mode 100644
index 0000000..57d19cd
--- /dev/null
+++ b/ui/angular/src/app/measure/create-measure/ac/ac.component.html
@@ -0,0 +1,467 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+-->
+<div class="container-fluid" (window:resize)="onResize($event)">
+ <div class="row">
+ <h5 class="over-title margin-bottom-15">Create Measure</h5>
+ </div>
+ <div class="row">
+ <form name="Form" id="form" (ngSubmit)="submit(acForm)" #acForm="ngForm" novalidate>
+ <div id="wizard" class="swMain" >
+ <ul>
+ <li (click)="goTo(1)" >
+ <a [ngClass]="{'selected' : currentStep >= 1, 'done' : currentStep > 1}" class="selected">
+ <div class="stepNumber">
+ 1
+ </div>
+ <span class="stepDesc text-small"> Choose Source </span>
+ </a>
+ </li>
+ <li (click)="goTo(2)">
+ <a [ngClass]="{'selected' : currentStep >= 2, 'done' : currentStep > 2}" class="" style="">
+ <div class="stepNumber">
+ 2
+ </div>
+ <span class="stepDesc text-small"> Choose Target </span>
+ </a>
+ </li>
+ <li (click)="goTo(3)">
+ <a [ngClass]="{'selected' : currentStep >= 3, 'done' : currentStep > 3}">
+ <div class="stepNumber">
+ 3
+ </div>
+ <span class="stepDesc text-small"> Mapping Source and Target </span>
+ </a>
+ </li>
+ <li (click)="goTo(4)">
+ <a [ngClass]="{'selected' : currentStep >= 4, 'done' : currentStep > 4}">
+ <div class="stepNumber">
+ 4
+ </div>
+ <span class="stepDesc text-small"> Configuration </span>
+ </a>
+ </li>
+ </ul>
+ </div>
+
+ <div id="step-1" *ngIf="currentStep == 1" class="formStep" >
+ <label class="stepDesc">This step let you choose the single source of truth for data quality comparision with target. Currently you can only select the attributes from one schema</label>
+ <div class="container-fluid">
+
+ <div class="col-md-4 col-lg-4 col-sm-4">
+ <fieldset>
+ <legend>Please select schema</legend>
+ <tree-root [nodes]="nodeList" [options]="options"></tree-root>
+ </fieldset>
+ </div>
+ <div class="col-md-8 col-lg-8 col-sm-8">
+ <fieldset>
+ <legend>
+ Select attributes
+ </legend>
+ <div class="y-scrollable">
+ <div>
+ <label>View schema:</label>
+ <i
+ style="color:#fff;font-weight: bold;">{{currentDB}}.{{currentTable}}
+ </i>
+ </div>
+ <div>
+ <table class="table table-striped">
+ <thead>
+ <tr style="background-color:#7D95CC">
+ <th><input type="checkbox" (click)="toggleAll()" [checked]="selectedAll"
+ /></th>
+ <th>Column Name</th>
+ <th>Type</th>
+ <th>Comment</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngIf="!schemaCollection || schemaCollection.length == 0">
+ <td colspan="5" style="text-align:center" ><span class="highlight">Please select a schema from the left tree first</span></td>
+ </tr>
+ <tr *ngFor="let row of schemaCollection">
+ <td>
+ <input type="checkbox" (click)='toggleSelection(row)' [checked]="row.selected" value={{row.name}}
+ />
+ </td>
+ <td>{{row.name}}</td>
+ <td>{{row.type}}</td>
+ <td>{{row.comment}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+ <div class="form-group btn-container">
+ <toaster-container></toaster-container>
+ <button class="btn btn-primary btn-o next-step btn-wide pull-right" (click)="next(Form)">
+ Next <i class="fa fa-arrow-circle-right"></i>
+ </button>
+ </div>
+ </div>
+ </div>
+ <div id="step-2" *ngIf="currentStep == 2" class="formStep" >
+ <label class="stepDesc">This step let you choose the target for data quality comparision with source</label>
+ <div class="container-fluid">
+ <div class="col-md-4 col-lg-4 col-sm-4">
+ <fieldset>
+ <legend>Please select schema</legend>
+ <tree-root [nodes]="nodeListTarget" [options]="targetOptions"></tree-root>
+ </fieldset>
+ </div>
+ <div class="col-md-8 col-lg-8 col-sm-8">
+ <fieldset>
+ <legend>
+ Select attributes
+ </legend>
+ <div class="y-scrollable">
+ <div>
+ <label>View schema:</label>
+ <i style="color:#fff;font-weight: bold;">{{currentDBTarget}}.{{currentTableTarget}}</i>
+ </div>
+ <div>
+ <table st-table="schemaCollectionTarget" class="table table-striped">
+ <thead>
+ <tr style="background-color:#7D95CC">
+ <th><input type="checkbox" (click)="toggleAllTarget()" [checked]="selectedAllTarget" /></th>
+ <th>Column Name</th>
+ <th>Type</th>
+ <th>Comment</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngIf="!schemaCollectionTarget || schemaCollectionTarget.length == 0">
+ <td colspan="5" style="text-align:center" ><span class="highlight">Please select a schema from the left tree first</span></td>
+ </tr>
+ <tr *ngFor="let row of schemaCollectionTarget">
+ <td>
+ <input type="checkbox" (click)='toggleSelectionTarget(row)' [checked]="row.selected"/>
+ </td>
+ <td>{{row.name}}</td>
+ <td>{{row.type}}</td>
+ <td>{{row.comment}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+ <div class="form-group btn-container" >
+ <button class="btn btn-primary btn-o back-step btn-wide pull-left" (click)="prev(Form)">
+ <i class="fa fa-arrow-circle-left"></i> Back
+ </button>
+ <toaster-container></toaster-container>
+ <button class="btn btn-primary btn-o next-step btn-wide pull-right" (click)="next(Form)">
+ Next <i class="fa fa-arrow-circle-right"></i>
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <div id="step-3" *ngIf="currentStep == 3" class="formStep" >
+ <label class="stepDesc">This step let you map the target data fields to source fields, you can choose the related fields from dropdown list of source</label>
+ <div class="container-fluid">
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <fieldset>
+ <legend>
+ Map the fields
+ </legend>
+
+ <div class="y-scrollable">
+
+ <div class="container col-md-12 col-lg-12 col-sm-12">
+ <table class="table table-striped">
+ <thead>
+ <tr style="background-color:#7D95CC;font-size:20px">
+ <th style="width:40%;">Target Fields</th>
+ <th style="width:10%;text-align:center">Map To</th>
+ <th style="width:40%;text-align:center">Source Fields</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let item of selectionTarget; let i=index" >
+ <td>{{currentDBTarget}}.{{currentTableTarget}}.{{item}}</td>
+ <td style="text-align:center;">
+ <select class="form-control" style="background:#00FFFF;font-weight:bold" id="mapRule" name='mapRule-{{i}}' [(ngModel)]="matches[i]" value="matches[i]">
+ <option *ngFor="let func of matchFunctions">{{func}}</option>
+ </select>
+ </td>
+ <td>
+ <select class="form-control" [(ngModel)] = 'mappings[i]' name="mappings{{i}}"
+ (ngModelChange)="addMapping($event,i)" value="mappings[i]">
+ <option
+ *ngFor="let itemSrc of selection"
+ [ngValue]="itemSrc"
+ >
+ {{currentDB}}.{{currentTable}}.{{itemSrc}}</option>
+ </select>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <p>
+ <label style="color:#B2C831">Accuracy Calculation Formula as Below:</label>
+ </p>
+
+ <div class="col-md-12 col-lg-12 col-sm-12" style="color:#fff;font-size:16px;display: flex;align-items: center">
+
+ <div class="" style="text-align:right;display:block;float:left;width:20%;">
+ Accuracy Rate(%) =
+ </div>
+ <div class="" style="text-align:center;display:block;float:left;margin:0 10px 0 10px">
+ <div class="formula-text-up" style="border-bottom:1px solid;">
+ Total Count of Matched records between <span class="badge">{{selectionTarget.length}}</span> <span style="color:green;">{{currentTableTarget}}</span> and <span class="badge">{{mappings.length}}
+ </span> <span style="color:green;">{{currentTable}}</span> fields
+ </div>
+ <div>
+ Total Count of records in <span style="color:green;font-weight:bold;">{{currentDB}}.{{currentTable}}</span>
+ </div>
+ </div>
+ <div class="" style="text-align:left;display:block;float:left;width:10%;">
+ x 100%
+ </div>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+
+ <div class="form-group btn-container" >
+ <button class="btn btn-primary btn-o back-step btn-wide pull-left" (click)="prev(Form)">
+ <i class="fa fa-arrow-circle-left"></i> Back
+ </button>
+ <toaster-container></toaster-container>
+ <button class="btn btn-primary btn-o next-step btn-wide pull-right" (click)="next(Form)">
+ Next <i class="fa fa-arrow-circle-right"></i>
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <div id="step-4" *ngIf="currentStep == 4" class="formStep" >
+ <label class="stepDesc">Please setup the measure required information</label>
+ <div class="container-fluid">
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <fieldset>
+ <legend>
+ Required Information
+ </legend>
+ <div class="y-scrollable">
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group" [ngClass]="{'has-error':acName.dirty&&acName.invalid, 'has-success':acName.valid}">
+ <label class="col-md-2 col-lg-2 col-sm-2 control-label">
+ Measure Name<span class="symbol required"></span>:
+ </label>
+
+ <div class="col-md-10 col-lg-10 col-sm-10 ">
+ <input type="text" class="form-control" [(ngModel)]="name" name="acName" #acName="ngModel" placeholder="Please input the measure name" required pattern="^[a-zA-Z0-9_-]*$" id="acName">
+ <span class="error text-small block "
+ *ngIf="acName.dirty && (acName.errors?.required)">
+ Measure Name is required</span>
+ <span class="error text-small block " *ngIf="acName.dirty && (acName.errors?.pattern)">Only letter, number, "-" and "_" are allowed</span>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group">
+ <label class="col-md-2 col-lg-2 col-sm-2 control-label">
+ Measure Description:
+ </label>
+
+ <div class="col-md-10 col-lg-10 col-sm-10 ">
+ <input type="text" class="form-control" [(ngModel)]="desc" placeholder="Please input detailed description of your measure" name="desc">
+ </div>
+ </div>
+ </div>
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group">
+ <label for="typeSelector" class="col-md-2 col-lg-2 col-sm-2 control-label">
+ Measure Type:
+ </label>
+ <div class="col-md-10 col-lg-10 col-sm-10 ">
+ <select id="typeSelector" class="form-control" [(ngModel)]="type" disabled required name="type">
+ <!-- <option *ngFor="let row of measureTypes" value="{{$index}}" >{{type}}</option> -->
+ <option>{{type}}</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group">
+ <label for="systemSelector" class="col-md-2 col-lg-2 col-sm-2 control-label">
+ Organization:
+ </label>
+ <div class="col-md-10 col-lg-10 col-sm-10 ">
+ <input type="text" id="systemSelector" class="form-control" [(ngModel)]="org" required ng-pattern="'([0-9a-zA-Z\\_\\-])+'" name="org">
+ </div>
+ </div>
+ </div>
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group">
+ <label class="col-md-2 col-lg-2 col-sm-2 control-label">
+ DataAsset:
+ </label>
+ <div class="col-md-10 col-lg-10 col-sm-10">
+ <input type="text" class="form-control" name="DataAsset"
+ value="{{currentTable}},{{currentTableTarget}}" disabled>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-12 col-lg-12 col-sm-12">
+ <div class="form-group">
+ <label class="col-md-2 col-lg-2 col-sm-2 control-label">
+ Owner:
+ </label>
+ <div class="col-md-10 col-lg-10 col-sm-10">
+ <input type="text" class="form-control" name="owner" disabled
+ [(ngModel)]="owner" >
+ </div>
+ </div>
+ </div>
+ </div>
+ <div style="color:#b2c831">
+ <p>
+ <i class="fa fa-info-circle"></i> After submitted, please go to "<a class="bark-link" routerLink="/measures">Measures</a>" to check the measure status
+ </p>
+ </div>
+
+ </fieldset>
+ </div>
+
+ <div class="form-group btn-container" >
+ <button class="btn btn-primary btn-o back-step btn-wide pull-left" (click)="prev(Form)">
+ <i class="fa fa-arrow-circle-left"></i> Back
+ </button>
+ <toaster-container></toaster-container>
+ <button type="submit" class="btn btn-primary btn-o next-step btn-wide pull-right">
+ Submit
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <div class="modal fade" id="confirm" role="dialog" #modal tabindex="-1" [ngClass]="{'in': visibleAnimate}"
+ [ngStyle]="{'display': visible ? 'block' : 'none', 'opacity': visibleAnimate ? 1 : 0}"
+ (click)="onContainerClicked($event)">
+ <div class="modal-dialog modal-xg modal-lg">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title">Save the measure with the below information?</h4>
+ </div>
+ <div class="modal-body">
+ <div class="container-fluid" id="viewruleContent" style="overflow:auto;">
+ <div class="row">
+ <h5 class="over-title margin-bottom-15">Basic information</h5>
+ </div>
+ <div class="row">
+ <div class="col-lg-12 col-md-12 col-sm-12">
+ <div id="viewrule-definition" class="viewrule-content">
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Measure Name:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 ">
+ {{name}}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Measure Description:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 ">
+ {{desc}}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Measure Type:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 ">
+ {{type}}
+ </div>
+ </div>
+ <div class="row">
+ <label for="systemSelector" class="col-md-4 col-lg-4 col-sm-4">
+ Organization:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8 ">
+ {{org}}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ DataAsset:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8">{{currentTable}},{{currentTableTarget}}
+ </div>
+ </div>
+ <div class="row">
+ <label class="col-md-4 col-lg-4 col-sm-4">
+ Owner:
+ </label>
+ <div class="col-md-8 col-lg-8 col-sm-8" >
+ {{owner}}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <br/>
+ <h5 class="row">Mapping rules</h5>
+ <div class="row">
+ <p>{{rules}}</p>
+ <p>
+ <label style="color:#B2C831">Accuracy Calculation Formula as Below:</label>
+ </p>
+ <div class="col-md-12 col-lg-12 col-sm-12" style="color:#fff;font-size:16px;display: flex; align-items: center">
+ <div class="" style="text-align:right;display:block;float:left;width:20%;">
+ Accuracy Rate(%) =
+ </div>
+ <div class="" style="text-align:center;display:block;float:left;margin:0 10px 0 10px">
+ <div class="formula-text-up" style="border-bottom:1px solid;">
+ Total Count of Matched records between <span class="badge">{{selectionTarget.length}}</span> <span style="color:green;">{{currentTableTarget}}</span> and <span class="badge">{{mappings.length}}
+ </span> <span style="color:green;">{{currentTable}}</span> fields
+ </div>
+ <div>
+ Total Count of records in <span style="color:green;font-weight:bold;">{{currentDB}}.{{currentTable}}</span>
+ </div>
+ </div>
+ <div class="" style="text-align:left;display:block;float:left;width:10%;">
+ x 100%
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" (click)="hide()">Cancel</button>
+ <button type="button" class="btn btn-primary" (click)="save()">Save</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/42ee8863/ui/angular/src/app/measure/create-measure/ac/ac.component.spec.ts
----------------------------------------------------------------------
diff --git a/ui/angular/src/app/measure/create-measure/ac/ac.component.spec.ts b/ui/angular/src/app/measure/create-measure/ac/ac.component.spec.ts
new file mode 100644
index 0000000..ee0762d
--- /dev/null
+++ b/ui/angular/src/app/measure/create-measure/ac/ac.component.spec.ts
@@ -0,0 +1,43 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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 { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AcComponent } from './ac.component';
+
+describe('AcComponent', () => {
+ let component: AcComponent;
+ let fixture: ComponentFixture<AcComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ AcComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AcComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should be created', () => {
+ expect(component).toBeTruthy();
+ });
+});