This commit is contained in:
insanity 2018-03-13 17:27:57 +09:00
parent c1a5179bfc
commit 65cb4454d8
20 changed files with 408 additions and 77 deletions

View File

@ -7,7 +7,7 @@
</button> </button>
<span class="spacer"></span> <span class="spacer"></span>
<of-notification [notifications]="toolbarHelpers?.notifications"></of-notification> <of-notification-badge [notifications]="toolbarHelpers?.notifications"></of-notification-badge>
<div class="menu-section"> <div class="menu-section">
<button mat-icon-button [matMenuTriggerFor]="countryMenu" aria-label="Open x-positioned menu"> <button mat-icon-button [matMenuTriggerFor]="countryMenu" aria-label="Open x-positioned menu">
<mat-icon>translate</mat-icon> <mat-icon>translate</mat-icon>

View File

@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { NotificationPageComponent } from './notification-page.component';
import { ListComponent as TargetListComponent } from 'packages/target/component/list/list.component';
const routes: Routes = [
{
path: '',
component: NotificationPageComponent,
children: [
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class NotificationPageRoutingModule { }

View File

@ -0,0 +1 @@
<of-notification></of-notification>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationPageComponent } from './notification-page.component';
describe('NotificationPageComponent', () => {
let component: NotificationPageComponent;
let fixture: ComponentFixture<NotificationPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NotificationPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NotificationPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,18 @@
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
@Component({
selector: 'of-pages-notification',
templateUrl: './notification-page.component.html',
styleUrls: ['./notification-page.component.scss']
})
export class NotificationPageComponent implements OnInit {
constructor(private route: ActivatedRoute, private router: Router) {
}
ngOnInit() {
}
}

View File

@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NotificationPageComponent } from './notification-page.component';
import { NotificationPageRoutingModule } from './notification-page-routing.module';
import { MaterialModule } from 'app/commons/ui/material/material.module';
import { NotificationModule } from 'packages/notification/notification.module';
@NgModule({
imports: [
CommonModule,
NotificationPageRoutingModule,
MaterialModule,
NotificationModule,
],
declarations: [
NotificationPageComponent,
]
})
export class NotificationPageModule { }

View File

@ -20,6 +20,7 @@ const routes: Routes = [
{ path: 'target', loadChildren: './target/target-page.module#TargetPageModule' }, { path: 'target', loadChildren: './target/target-page.module#TargetPageModule' },
{ path: 'overview', loadChildren: './overview/overview-page.module#OverviewPageModule' }, { path: 'overview', loadChildren: './overview/overview-page.module#OverviewPageModule' },
{ path: 'dashboard', loadChildren: './dashboard/dashboard-page.module#DashboardPageModule' }, { path: 'dashboard', loadChildren: './dashboard/dashboard-page.module#DashboardPageModule' },
{ path: 'notification', loadChildren: './notification/notification-page.module#NotificationPageModule' },
{ path: 'settings/member', loadChildren: './settings/member/member-page.module#MemberPageModule' }, { path: 'settings/member', loadChildren: './settings/member/member-page.module#MemberPageModule' },
] ]
} }

View File

@ -0,0 +1,52 @@
<div class="toolbar-notification-container">
<button mat-icon-button (click)="isOpen = !isOpen;" [ngClass]="[cssPrefix+'-btn']" [class.open]="isOpen">
<mat-icon>notifications_none</mat-icon>
<span class="badge" *ngIf="notifications && notifications?.length !== 0">{{ notifications?.length }}</span>
</button>
<div class="dropdown mat-elevation-z4" [class.open]="isOpen">
<div class="card">
<div class="header" fxLayout="row" fxLayoutAlign="space-between center">
<div class="title">
<div class="name">Notifications</div>
<div class="extra">
<button mat-button (click)="handleMarkAllAsRead()">Mark all as read</button>
</div>
</div>
<button type="button" mat-icon-button>
<mat-icon class="icon">settings</mat-icon>
</button>
</div>
<div *ngIf="notifications?.length !== 0; then thenBlock else elseBlock;"></div>
<div class="footer" fxLayout="row" fxLayoutAlign="center center">
<div class="action" (click)="handleViewAll()">View All</div>
</div>
</div>
</div>
</div>
<ng-template #thenBlock>
<perfect-scrollbar class="content">
<div *ngFor="let notification of notifications; last as isLast">
<div class="notification" fxLayout="row" fxLayoutAlign="start center" mat-ripple>
<mat-icon class="icon">notifications</mat-icon>
<div class="title" fxLayout="column">
<div class="name">{{ notification.title }}</div>
<div class="time">{{ notification.lastTime }}</div>
</div>
<span fxFlex></span>
<button type="button" mat-icon-button (click)="delete(notification)">
<mat-icon class="close">close</mat-icon>
</button>
</div>
<div class="divider" *ngIf="!isLast"></div>
</div>
</perfect-scrollbar>
</ng-template>
<!-- <ng-template #elseBlock> -->
<!-- <div class="no" fxLayout="row" fxLayoutAlign="center center">No notification found.</div>
</ng-template> -->

View File

@ -0,0 +1,165 @@
$prefix: 'notification';
.badge {
position: absolute;
top: 0;
left: 50%;
font-weight: 700;
line-height: 13px;
height: 13px;
padding: 5px;
border-radius: 26%;
width: 30%;
background-color: #f44336;
color: #fff;
border-color:#f44336
}
.#{$prefix} {
&-container {
position: relative;
display: flex;
align-items: center;
}
&-btn {
display: flex;
justify-content: center;
margin-right: 10px;
}
}
.dropdown {
background: white;
position: absolute;
top: 42px;
right: 28px;
min-width: 350px;
z-index: 2;
transform: translateY(0) scale(0);
transform-origin: top right;
visibility: hidden;
transition: transform .4s cubic-bezier(.25, .8, .25, 1), visibility .4s cubic-bezier(.25, .8, .25, 1);
@media screen and (max-width: 599px) {
min-width: 50vw;
right: 5px;
transform: translateY(0);
visibility: hidden;
transition: transform .4s cubic-bezier(.25,.8,.25,1), visibility .4s cubic-bezier(.25,.8,.25,1);
}
&.open {
transform: translateY(0) scale(1);
visibility: visible;
}
.card {
.header {
background: #EEEEEE;
min-height: 54px;
padding-left: 16px;
padding-right: 8px;
color: #555;
display: flex;
justify-content: flex-start;
align-items: center;
align-content: center;
border-bottom: 1px solid #e0e0e0;
.extra {
font-size: 12px;
color: #888;
}
}
}
.content {
overflow: hidden;
max-height: 256px;
.notification {
min-height: 64px;
padding: 0 16px 0 14px;
position: relative;
color: #666;
cursor: pointer;
.icon {
height: 28px;
width: 28px;
line-height: 28px;
font-size: 18px;
margin-right: 13px;
text-align: center;
border-radius: 50%;
background: #FFF;
color: #888;
border: 1px solid #EEE;
}
.title {
font-weight: 500;
font-size: 14px;
}
.time {
font-size: 12px;
}
.close {
font-size: 18px;
width: 18px;
height: 18px;
line-height: 18px;
}
&.primary {
.icon {
background: #ccc;
color: #ddd;
}
}
&.accent {
.icon {
background: #aaa;
color: #bbb;
}
}
&.warn {
.icon {
background: #eee;
color: #ddd;
}
}
&.read {
color: #999;
.name {
font-weight: normal;
}
}
}
}
.footer {
min-height: 42px;
border-top: 1px solid #EEE;
.action {
cursor: pointer;
color: #AAA;
text-align: center;
font-size: 13px;
}
}
.divider {
width: calc(100% - 30px);
height: 1px;
background: #EEE;
margin: 0 16px 0 14px;
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationBadgeComponent } from './notification.component';
describe('NotificationBadgeComponent', () => {
let component: NotificationBadgeComponent;
let fixture: ComponentFixture<NotificationBadgeComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NotificationBadgeComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NotificationBadgeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,38 @@
import { Component, OnInit, Input } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'of-notification-badge',
templateUrl: './notification.component.html',
styleUrls: ['./notification.component.scss']
})
export class NotificationBadgeComponent implements OnInit {
cssPrefix = 'toolbar-notification';
isOpen = false;
@Input() notifications = [];
constructor(
private router: Router
) { }
ngOnInit() {
}
select() {
}
delete(notification) {
}
handleMarkAllAsRead() {
console.log('Mark All');
}
handleViewAll() {
this.isOpen = false;
this.router.navigate(['notification']);
}
}

View File

@ -1,5 +1,7 @@
import { NotificationComponent } from './notification/notification.component'; import { NotificationComponent } from './notification/notification.component';
import { NotificationBadgeComponent } from './badge/notification.component';
export const COMPONENTS = [ export const COMPONENTS = [
NotificationComponent, NotificationComponent,
NotificationBadgeComponent,
]; ];

View File

@ -1,52 +1 @@
<div>list</div>
<div class="toolbar-notification-container">
<button mat-icon-button (click)="isOpen = !isOpen;" [ngClass]="[cssPrefix+'-btn']" [class.open]="isOpen">
<mat-icon>notifications_none</mat-icon>
<span class="badge" *ngIf="notifications && notifications?.length !== 0">{{ notifications?.length }}</span>
</button>
<div class="dropdown mat-elevation-z4" [class.open]="isOpen">
<div class="card">
<div class="header" fxLayout="row" fxLayoutAlign="space-between center">
<div class="title">
<div class="name">Notifications</div>
<div class="extra">
You have {{ notifications?.length }} new notifications</div>
</div>
<button type="button" mat-icon-button>
<mat-icon class="icon">settings</mat-icon>
</button>
</div>
<div *ngIf="notifications?.length !== 0; then thenBlock else elseBlock;"></div>
<div class="footer" fxLayout="row" fxLayoutAlign="center center">
<div class="action">Mark all as read</div>
</div>
</div>
</div>
</div>
<ng-template #thenBlock>
<perfect-scrollbar class="content">
<div *ngFor="let notification of notifications; last as isLast">
<div class="notification" fxLayout="row" fxLayoutAlign="start center" mat-ripple>
<mat-icon class="icon">notifications</mat-icon>
<div class="title" fxLayout="column">
<div class="name">{{ notification.title }}</div>
<div class="time">{{ notification.lastTime }}</div>
</div>
<span fxFlex></span>
<button type="button" mat-icon-button (click)="delete(notification)">
<mat-icon class="close">close</mat-icon>
</button>
</div>
<div class="divider" *ngIf="!isLast"></div>
</div>
</perfect-scrollbar>
</ng-template>
<ng-template #elseBlock>
<div class="no" fxLayout="row" fxLayoutAlign="center center">????</div>
</ng-template>

View File

@ -1,4 +1,5 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
import { Router } from '@angular/router';
@Component({ @Component({
selector: 'of-notification', selector: 'of-notification',
@ -7,21 +8,11 @@ import { Component, OnInit, Input } from '@angular/core';
}) })
export class NotificationComponent implements OnInit { export class NotificationComponent implements OnInit {
cssPrefix = 'toolbar-notification'; constructor(
isOpen = false; private router: Router
@Input() notifications = []; ) { }
constructor() { }
ngOnInit() { ngOnInit() {
} }
select() {
}
delete(notification) {
}
} }

View File

@ -1,4 +1,4 @@
<div><h3>{{probeAlias}}</h3></div> <!-- <div><h3>{{probeAlias}}</h3></div>
<div fxLayout="row" fxLayoutWrap fxLayoutAlign="space-between center"> <div fxLayout="row" fxLayoutWrap fxLayoutAlign="space-between center">
<mat-card [style.padding]="'15px 50px'">Status: UP</mat-card> <mat-card [style.padding]="'15px 50px'">Status: UP</mat-card>
@ -20,4 +20,5 @@
<mat-card fxFlex="30%" fxFlex.lt-sm="100" fxFlex.sm="30"> <mat-card fxFlex="30%" fxFlex.lt-sm="100" fxFlex.sm="30">
<of-info-table [title]="'Title3'" [data]="probeInfo"></of-info-table> <of-info-table [title]="'Title3'" [data]="probeInfo"></of-info-table>
</mat-card> </mat-card>
</div> </div> -->
<div>aaa</div>

View File

@ -1,14 +1,22 @@
import { Component, OnInit, Inject } from '@angular/core'; import { Component, OnInit, Inject, AfterContentInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { ConfirmDialogComponent } from 'app/commons/component/confirm-dialog/confirm-dialog.component'; import { ConfirmDialogComponent } from 'app/commons/component/confirm-dialog/confirm-dialog.component';
import * as DetailStore from '../../store/detail';
import { DetailSelector } from '../../store';
import { Store, select } from '@ngrx/store';
import { Probe } from '../../model';
import { RPCError } from 'packages/core/rpc/error';
@Component({ @Component({
selector: 'of-probe-detail', selector: 'of-probe-detail',
templateUrl: './detail.component.html', templateUrl: './detail.component.html',
styleUrls: ['./detail.component.scss'] styleUrls: ['./detail.component.scss']
}) })
export class DetailComponent implements OnInit { export class DetailComponent implements OnInit, AfterContentInit {
probe$ = this.store.pipe(select(DetailSelector.select('probe')));
probe: Probe = null;
probeAlias = ''; probeAlias = '';
probeId = undefined; probeId = undefined;
@ -67,6 +75,7 @@ export class DetailComponent implements OnInit {
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
public dialog: MatDialog, public dialog: MatDialog,
private store: Store<DetailStore.State>
) { } ) { }
ngOnInit() { ngOnInit() {
@ -74,6 +83,23 @@ export class DetailComponent implements OnInit {
this.probeAlias = 'Probe Alias 블라블라'; this.probeAlias = 'Probe Alias 블라블라';
} }
ngAfterContentInit() {
this.store.dispatch(
new DetailStore.Read(
{ id: this.probeId }
)
);
this.probe$.subscribe(
(probe: Probe) => {
console.log(probe);
this.probe = probe;
},
(error: RPCError) => {
console.log(error.message);
}
);
}
handleStartStop() { handleStartStop() {
this.isUpState = !this.isUpState; this.isUpState = !this.isUpState;
} }

View File

@ -1,8 +1,7 @@
import { MetaProbeStatus } from 'packages/meta/model'; import { MetaProbeStatus } from 'packages/meta/model';
import { Domain } from 'packages/domain/model'; import { Domain } from 'packages/domain/model';
import { Member } from 'packages/member/model'; import { Member } from 'packages/member/model';
import { Target } from 'packages/target/model'; import { Infra } from 'packages/infra/model';
import { InfraHost } from 'packages/infra/model';
export interface Probe { export interface Probe {
id?: number; id?: number;
@ -17,5 +16,5 @@ export interface Probe {
authorizeDate?: Date; authorizeDate?: Date;
authorizeMember?: Member; authorizeMember?: Member;
// host?: InfraHost; // host?: InfraHost;
targets?: Target[]; targets?: Infra[];
} }

View File

@ -9,7 +9,6 @@ import {
import { import {
State, State,
initialState, initialState,
adapter,
} from './detail.state'; } from './detail.state';
import { Probe } from '../../model'; import { Probe } from '../../model';

View File

@ -14,9 +14,6 @@ import { Probe } from '../../model';
// probe: null, // probe: null,
// }; // };
// export const getProbe = (state: State) => state.probe;
// export const getError = (state: State) => state.error;
// export const isPending = (state: State) => state.isPending;
import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';
@ -32,6 +29,10 @@ export const initialState: State = adapter.getInitialState({
probe: null, probe: null,
}); });
export const getProbe = (state: State) => state.probe;
export const getError = (state: State) => state.error;
export const isPending = (state: State) => state.isPending;
// export const { // export const {
// selectIds, // selectIds,
// selectEntities, // selectEntities,