skeleton branch

This commit is contained in:
Sercan Yemen 2017-08-22 15:55:48 +03:00
parent 8cbc2f3ab7
commit d7003711ee
288 changed files with 75 additions and 19335 deletions

View File

@ -10,44 +10,17 @@ import 'hammerjs';
import { SharedModule } from './core/modules/shared.module'; import { SharedModule } from './core/modules/shared.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { ProjectModule } from './main/content/apps/dashboards/project/project.module'; import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { PerfectScrollbarConfigInterface, PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { FuseFakeDbService } from './fuse-fake-db/fuse-fake-db.service'; import { FuseFakeDbService } from './fuse-fake-db/fuse-fake-db.service';
import { FuseMainModule } from './main/main.module'; import { FuseMainModule } from './main/main.module';
import { PagesModule } from './main/content/pages/pages.module';
import { UIModule } from './main/content/ui/ui.module';
import { ComponentsModule } from './main/content/components/components.module';
import { FuseSplashScreenService } from './core/services/splash-screen.service'; import { FuseSplashScreenService } from './core/services/splash-screen.service';
import { FuseConfigService } from './core/services/config.service'; import { FuseConfigService } from './core/services/config.service';
import { FuseSampleModule } from './main/content/sample/sample.module';
const appRoutes: Routes = [ const appRoutes: Routes = [
{
path : 'apps/mail',
loadChildren: './main/content/apps/mail/mail.module#FuseMailModule'
},
{
path : 'apps/chat',
loadChildren: './main/content/apps/chat/chat.module#FuseChatModule'
},
{
path : 'apps/calendar',
loadChildren: './main/content/apps/calendar/calendar.module#FuseCalendarModule'
},
{
path : 'apps/todo',
loadChildren: './main/content/apps/todo/todo.module#FuseTodoModule'
},
{
path : 'apps/file-manager',
loadChildren: './main/content/apps/file-manager/file-manager.module#FuseFileManagerModule'
},
{
path : 'apps/contacts',
loadChildren: './main/content/apps/contacts/contacts.module#FuseContactsModule'
},
{ {
path : '**', path : '**',
redirectTo: 'apps/dashboards/project' redirectTo: 'sample'
} }
]; ];
@ -62,20 +35,12 @@ const appRoutes: Routes = [
BrowserAnimationsModule, BrowserAnimationsModule,
RouterModule.forRoot(appRoutes), RouterModule.forRoot(appRoutes),
SharedModule, SharedModule,
InMemoryWebApiModule.forRoot(FuseFakeDbService, {delay: 125}), InMemoryWebApiModule.forRoot(FuseFakeDbService, {delay: 125}),
PerfectScrollbarModule.forRoot(), PerfectScrollbarModule.forRoot(),
FuseMainModule, FuseMainModule,
FuseSampleModule
ProjectModule,
PagesModule,
UIModule,
ComponentsModule
], ],
providers: [ providers : [
FuseSplashScreenService, FuseSplashScreenService,
FuseConfigService FuseConfigService
], ],

View File

@ -1,413 +0,0 @@
export class FuseNavigation
{
public items: any[];
constructor()
{
this.items = [
{
'title': 'APPS',
'type' : 'subheader'
},
{
'title' : 'Dashboards',
'type' : 'nav-collapse',
'icon' : 'dashboard',
'children': [
{
'type' : 'nav-item',
'title': 'Project',
'url' : '/apps/dashboards/project'
}
]
},
{
'title': 'Calendar',
'type' : 'nav-item',
'icon' : 'today',
'url' : '/apps/calendar'
},
{
'title': 'Mail',
'type' : 'nav-item',
'icon' : 'email',
'url' : '/apps/mail',
'badge': {
'title': 25,
'bg' : '#F44336',
'fg' : '#FFFFFF'
}
},
{
'title': 'Chat',
'type' : 'nav-item',
'icon' : 'chat',
'url' : '/apps/chat',
'badge': {
'title': 13,
'bg' : '#09d261',
'fg' : '#FFFFFF'
}
},
{
'title': 'File Manager',
'type' : 'nav-item',
'icon' : 'folder',
'url' : '/apps/file-manager'
},
{
'title': 'Contacts',
'type' : 'nav-item',
'icon' : 'account_box',
'url' : '/apps/contacts'
},
{
'title': 'To-Do',
'type' : 'nav-item',
'icon' : 'check_box',
'url' : '/apps/todo',
'badge': {
'title': 3,
'bg' : '#FF6F00',
'fg' : '#FFFFFF'
}
},
{
'title': 'PAGES',
'type' : 'subheader'
},
{
'title' : 'Authentication',
'type' : 'nav-collapse',
'icon' : 'lock',
'children': [
{
'title': 'Login',
'type' : 'nav-item',
'url' : '/pages/auth/login'
},
{
'title': 'Login v2',
'type' : 'nav-item',
'url' : '/pages/auth/login-2'
},
{
'title': 'Register',
'type' : 'nav-item',
'url' : '/pages/auth/register'
},
{
'title': 'Register v2',
'type' : 'nav-item',
'url' : '/pages/auth/register-2'
},
{
'title': 'Forgot Password',
'type' : 'nav-item',
'url' : '/pages/auth/forgot-password'
},
{
'title': 'Reset Password',
'type' : 'nav-item',
'url' : '/pages/auth/reset-password'
},
{
'title': 'Lock Screen',
'type' : 'nav-item',
'url' : '/pages/auth/lock'
}
]
},
{
'title': 'Coming Soon',
'type' : 'nav-item',
'icon' : 'alarm',
'url' : '/pages/coming-soon'
},
{
'title' : 'Errors',
'type' : 'nav-collapse',
'icon' : 'error',
'children': [
{
'title': '404',
'type' : 'nav-item',
'url' : '/pages/errors/error-404'
},
{
'title': '500',
'type' : 'nav-item',
'url' : '/pages/errors/error-500'
}
]
},
{
'title' : 'Invoice',
'type' : 'nav-collapse',
'icon' : 'receipt',
'children': [
{
'title': 'Modern',
'type' : 'nav-item',
'url' : '/pages/invoices/modern'
},
{
'title': 'Compact',
'type' : 'nav-item',
'url' : '/pages/invoices/compact'
}
]
},
{
'title': 'Maintenance',
'type' : 'nav-item',
'icon' : 'build',
'url' : '/pages/maintenance'
},
{
'title': 'Profile',
'type' : 'nav-item',
'icon' : 'person',
'url' : '/pages/profile'
},
{
'title': 'Search',
'type' : 'nav-item',
'icon' : 'search',
'url' : '/pages/search'
},
{
'title': 'USER INTERFACE',
'type' : 'subheader'
},
/*{
'title' : 'Elements',
'type' : 'nav-collapse',
'icon' : 'layers',
'children': [
{
'title': 'Alerts',
'type' : 'nav-item',
'url' : '/ui/elements/alerts'
},
{
'title': 'Badges',
'type' : 'nav-item',
'url' : '/ui/elements/badges'
},
{
'title': 'Breadcrumb',
'type' : 'nav-item',
'url' : '/ui/elements/breadcrumb'
},
{
'title': 'Buttons',
'type' : 'nav-item',
'url' : '/ui/elements/buttons'
},
{
'title': 'Button Group',
'type' : 'nav-item',
'url' : '/ui/elements/button-group'
},
{
'title': 'Cards',
'type' : 'nav-item',
'url' : '/ui/elements/cards'
},
{
'title': 'Dropdowns',
'type' : 'nav-item',
'url' : '/ui/elements/dropdowns'
},
{
'title': 'Forms',
'type' : 'nav-item',
'url' : '/ui/elements/forms'
},
{
'title': 'Input Group',
'type' : 'nav-item',
'url' : '/ui/elements/input-group'
},
{
'title': 'Jumbotron',
'type' : 'nav-item',
'url' : '/ui/elements/jumbotron'
},
{
'title': 'List Group',
'type' : 'nav-item',
'url' : '/ui/elements/list-group'
},
{
'title': 'Navs',
'type' : 'nav-item',
'url' : '/ui/elements/navs'
},
{
'title': 'Navbar',
'type' : 'nav-item',
'url' : '/ui/elements/navbar'
},
{
'title': 'Pagination',
'type' : 'nav-item',
'url' : '/ui/elements/pagination'
},
{
'title': 'Progress',
'type' : 'nav-item',
'url' : '/ui/elements/progress'
}
]
},*/
{
'title': 'Forms',
'type' : 'nav-item',
'icon' : 'web_asset',
'url' : '/ui/forms'
},
{
'title': 'Icons',
'type' : 'nav-item',
'icon' : 'photo',
'url' : '/ui/icons'
},
{
'title': 'Typography',
'type' : 'nav-item',
'icon' : 'text_fields',
'url' : '/ui/typography'
},
{
'title': 'Helper Classes',
'type' : 'nav-item',
'icon' : 'help',
'url' : '/ui/helper-classes'
},
{
'title' : 'Page Layouts',
'type' : 'nav-collapse',
'icon' : 'view_quilt',
'children': [
{
'title' : 'Carded',
'type' : 'nav-collapse',
'children': [
{
'title': 'Full Width',
'type' : 'nav-item',
'url' : '/ui/page-layouts/carded/full-width'
},
{
'title': 'Full Width 2',
'type' : 'nav-item',
'url' : '/ui/page-layouts/carded/full-width-2'
},
{
'title': 'Left Sidenav',
'type' : 'nav-item',
'url' : '/ui/page-layouts/carded/left-sidenav'
},
{
'title': 'Left Sidenav 2',
'type' : 'nav-item',
'url' : '/ui/page-layouts/carded/left-sidenav-2'
},
{
'title': 'Right Sidenav',
'type' : 'nav-item',
'url' : '/ui/page-layouts/carded/right-sidenav'
},
{
'title': 'Right Sidenav 2',
'type' : 'nav-item',
'url' : '/ui/page-layouts/carded/right-sidenav-2'
}
]
},
{
'title' : 'Simple',
'type' : 'nav-collapse',
'children': [
{
'title': 'Full Width',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/full-width'
},
{
'title': 'Left Sidenav',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/left-sidenav'
},
{
'title': 'Left Sidenav 2',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/left-sidenav-2'
},
{
'title': 'Left Sidenav 3',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/left-sidenav-3'
},
{
'title': 'Right Sidenav',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/right-sidenav'
},
{
'title': 'Right Sidenav 2',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/right-sidenav-2'
},
{
'title': 'Right Sidenav 3',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/right-sidenav-3'
},
{
'title': 'Tabbed',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/tabbed'
}
]
},
{
'title': 'Blank',
'type' : 'nav-item',
'url' : '/ui/page-layouts/blank'
}
]
},
{
'title': 'Colors',
'type' : 'nav-item',
'icon' : 'color_lens',
'url' : '/ui/colors'
},
{
'title': 'COMPONENTS',
'type' : 'subheader'
},
{
'title' : 'Datatables',
'type' : 'nav-collapse',
'icon' : 'border_all',
'children': [
{
'title': 'ngx-datatable',
'type' : 'nav-item',
'url' : '/components/datatables/ngx-datatable'
}
]
},
{
'title': 'Price Tables',
'type' : 'nav-item',
'icon' : 'view_carousel',
'url' : '/components/price-tables'
}
];
}
}

View File

@ -1,5 +1,5 @@
import { EventEmitter, Injectable } from '@angular/core'; import { EventEmitter, Injectable } from '@angular/core';
import { FuseNavigation } from './navigation.model'; import { FuseNavigation } from '../../../navigation.model';
@Injectable() @Injectable()
export class FuseNavigationService export class FuseNavigationService

View File

@ -1,125 +0,0 @@
<div id="calendar" class="page-layout simple fullwidth" perfect-scrollbar>
<!-- HEADER -->
<div class="header" [ngClass]="viewDate | date:'MMM'">
<div class="header-content" fxLayout="column" fxLayoutAlign="space-between">
<div class="header-top" fxLayout="row" fxLayoutAlign="space-between center" fxLayout.xs="column">
<div class="logo mb-16 mb-sm-0" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="logo-icon">today</md-icon>
<span class="logo-text">Calendar</span>
</div>
<!-- TOOLBAR -->
<div class="toolbar" fxLayout="row" fxLayoutAlign="start center">
<button md-button class="mat-icon-button" aria-label="Search" md-tooltip="Search">
<md-icon>search</md-icon>
</button>
<button md-button class="mat-icon-button"
mwlCalendarToday
[(viewDate)]="viewDate"
(viewDateChange)="selectedDay = {date:$event}"
aria-label="Today" md-tooltip="Today">
<!--(click)="selectedDay = viewDate"-->
<md-icon>today</md-icon>
</button>
<button md-button class="mat-icon-button" (click)="view='day'"
aria-label="Day" md-tooltip="Day">
<md-icon>view_day</md-icon>
</button>
<button md-button class="mat-icon-button" (click)="view='week'"
aria-label="Week" md-tooltip="Week">
<md-icon>view_week</md-icon>
</button>
<button md-button class="mat-icon-button" (click)="view='month'"
aria-label="Month" md-tooltip="Month">
<md-icon>view_module</md-icon>
</button>
</div>
</div>
<!-- / TOOLBAR -->
<!-- HEADER BOTTOM -->
<div class="header-bottom" fxLayout="row" fxLayoutAlign="center center">
<button md-button class="mat-icon-button arrow"
mwlCalendarPreviousView
[view]="view"
[(viewDate)]="viewDate"
(viewDateChange)="selectedDay = {date:$event}"
aria-label="Previous">
<md-icon>chevron_left</md-icon>
</button>
<div class="title">
{{ viewDate | calendarDate:(view + 'ViewTitle'):'en' }}
</div>
<button md-button class="mat-icon-button arrow"
mwlCalendarNextView
[view]="view"
[(viewDate)]="viewDate"
(viewDateChange)="selectedDay = {date:$event}"
aria-label="Next">
<md-icon>chevron_right</md-icon>
</button>
</div>
<!-- / HEADER BOTTOM -->
</div>
<!-- ADD EVENT BUTTON -->
<button md-fab class="add-event-button mat-warn" (click)="addEvent($event)" aria-label="Add event">
<md-icon>add</md-icon>
</button>
<!-- / ADD EVENT BUTTON -->
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content" perfect-scrollbar>
<div [ngSwitch]="view">
<mwl-calendar-month-view
*ngSwitchCase="'month'"
[viewDate]="viewDate"
[events]="events"
[refresh]="refresh"
[activeDayIsOpen]="activeDayIsOpen"
(dayClicked)="dayClicked($event.day)"
(eventClicked)="editEvent('edit', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)"
(beforeViewRender)="beforeMonthViewRender($event)">
</mwl-calendar-month-view>
<mwl-calendar-week-view
*ngSwitchCase="'week'"
[viewDate]="viewDate"
(viewDateChange)="selectedDay = {date:$event}"
[events]="events"
[refresh]="refresh"
(dayClicked)="dayClicked($event.day)"
(eventClicked)="editEvent('edit', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-week-view>
<mwl-calendar-day-view
*ngSwitchCase="'day'"
[viewDate]="viewDate"
(viewDateChange)="selectedDay = {date:$event}"
[events]="events"
[refresh]="refresh"
(dayClicked)="dayClicked($event.day)"
(eventClicked)="editEvent('edit', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-day-view>
</div>
</div>
<!-- / CONTENT -->
</div>

View File

@ -1,307 +0,0 @@
@import "src/app/core/scss/fuse";
@import "node_modules/angular-calendar/scss/angular-calendar";
.cal-month-view {
.cal-header {
.cal-cell {
font-weight: 500;
}
}
.cal-day-cell {
@include media-breakpoint(lg) {
min-height: 150px;
}
@include media-breakpoint(gt-lg) {
min-height: 200px;
}
&.cal-open {
@include mat-elevation(3);
}
}
.cal-open-day-events {
background: whitesmoke;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.13);
padding: 0;
display: flex;
flex-direction: column;
> div {
padding: 0 16px;
margin: 8px 16px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: start;
background-color: white;
@include mat-elevation(1);
transition: box-shadow 300ms ease;
&:first-of-type {
margin-top: 24px;
}
&:last-of-type {
margin-bottom: 24px;
}
&:hover {
@include mat-elevation(3);
}
.cal-event {
top: 0;
margin: 0;
}
mwl-calendar-event-title {
flex: 1;
.cal-event-title {
display: block;
padding: 21px 24px;
line-height: 1;
text-decoration: none;
color: black;
}
}
mwl-calendar-event-actions {
.cal-event-actions {
display: flex;
flex-direction: row;
align-items: center;
.cal-event-action {
display: block;
line-height: 1;
padding: 8px;
}
}
}
}
}
}
.cal-week-view {
.cal-header > b {
font-weight: 500;
}
.cal-event {
display: flex;
flex-direction: row;
align-items: center;
mwl-calendar-event-title {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
order: 0;
.cal-event-title {
display: block;
//padding: 21px 24px;
line-height: 1;
text-decoration: none;
color: black;
}
}
mwl-calendar-event-actions {
order: 1;
.cal-event-actions {
display: flex;
flex-direction: row;
align-items: center;
.cal-event-action {
display: block;
line-height: 1;
padding: 8px;
}
}
}
}
}
.cal-day-view {
.cal-time {
font-weight: 500;
}
.cal-event {
display: flex;
flex-direction: row;
mwl-calendar-event-title {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
order: 0;
.cal-event-title {
display: block;
height: 26px;
line-height: 26px;
text-decoration: none;
color: black;
}
}
mwl-calendar-event-actions {
order: 1;
.cal-event-actions {
display: flex;
flex-direction: row;
align-items: center;
.cal-event-action {
display: block;
line-height: 1;
padding: 4px;
}
}
}
}
}
#calendar {
background: #FFFFFF;
.header {
height: 200px;
min-height: 200px;
max-height: 200px;
padding: 24px;
position: relative;
background-size: 100% auto;
background-position: 0 50%;
background-repeat: no-repeat;
background-color: #FAFAFA;
color: #FFFFFF;
&:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
background: rgba(0, 0, 0, 0.45);
}
&.Jan {
background-image: url('/assets/images/backgrounds/january.jpg');
background-position: 0 45%;
}
&.Feb {
background-image: url('/assets/images/backgrounds/february.jpg');
background-position: 0 50%;
}
&.Mar {
background-image: url('/assets/images/backgrounds/march.jpg');
background-position: 0 45%;
}
&.Apr {
background-image: url('/assets/images/backgrounds/april.jpg');
background-position: 0 48%;
}
&.May {
background-image: url('/assets/images/backgrounds/may.jpg');
background-position: 0 47%;
}
&.Jun {
background-image: url('/assets/images/backgrounds/june.jpg');
background-position: 0 48%;
}
&.Jul {
background-image: url('/assets/images/backgrounds/july.jpg');
background-position: 0 3%;
}
&.Aug {
background-image: url('/assets/images/backgrounds/august.jpg');
background-position: 0 61%;
}
&.Sep {
background-image: url('/assets/images/backgrounds/september.jpg');
background-position: 0 58%;
}
&.Oct {
background-image: url('/assets/images/backgrounds/october.jpg');
background-position: 0 50%;
}
&.Nov {
background-image: url('/assets/images/backgrounds/november.jpg');
background-position: 0 46%;
}
&.Dec {
background-image: url('/assets/images/backgrounds/december.jpg');
background-position: 0 43%;
}
.header-content {
height: 100%;
.header-top {
position: relative;
z-index: 2;
.logo {
.logo-icon {
margin-right: 16px;
}
.logo-text {
font-size: 24px;
}
}
}
.header-bottom {
position: relative;
z-index: 2;
.title {
font-size: 20px;
min-width: 160px;
text-align: center;
font-weight: 500;
}
}
}
.add-event-button {
position: absolute;
right: 18px;
bottom: -32px;
z-index: 10;
}
md-icon {
color: #FFFFFF;
}
}
.content {
padding: 24px;
}
}

View File

@ -1,267 +0,0 @@
import { startOfDay, endOfDay, subDays, addDays, endOfMonth, isSameDay, isSameMonth, addHours } from 'date-fns';
import { ChangeDetectionStrategy, Component, OnInit, ViewEncapsulation } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { MdDialog, MdDialogRef } from '@angular/material';
import { FuseCalendarEventFormDialogComponent } from './event-form/event-form.component';
import { FormGroup } from '@angular/forms';
import { CalendarEventModel } from './event.model';
import { CalendarService } from './calendar.service';
import {
CalendarEvent,
CalendarEventAction,
CalendarEventTimesChangedEvent,
CalendarMonthViewDay
} from 'angular-calendar';
import { FuseConfirmDialogComponent } from '../../../../core/components/confirm-dialog/confirm-dialog.component';
@Component({
selector : 'fuse-calendar',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl : './calendar.component.html',
styleUrls : ['./calendar.component.scss'],
encapsulation : ViewEncapsulation.None
})
export class FuseCalendarComponent implements OnInit
{
view: string;
viewDate: Date;
events: CalendarEvent[];
public actions: CalendarEventAction[];
activeDayIsOpen: boolean;
refresh: Subject<any> = new Subject();
dialogRef: any;
confirmDialogRef: MdDialogRef<FuseConfirmDialogComponent>;
selectedDay: any;
constructor(
public dialog: MdDialog,
public calendarService: CalendarService
)
{
this.view = 'month';
this.viewDate = new Date();
this.activeDayIsOpen = true;
this.selectedDay = {date: startOfDay(new Date())};
this.actions = [
{
label : '<i class="material-icons s-16">edit</i>',
onClick: ({event}: { event: CalendarEvent }): void => {
this.editEvent('edit', event);
}
},
{
label : '<i class="material-icons s-16">delete</i>',
onClick: ({event}: { event: CalendarEvent }): void => {
this.deleteEvent(event);
}
}
];
/**
* Get events from service/server
*/
this.setEvents();
}
ngOnInit()
{
/**
* Watch re-render-refresh for updating db
*/
this.refresh.subscribe(updateDB => {
// console.warn('REFRESH');
if ( updateDB )
{
// console.warn('UPDATE DB');
this.calendarService.updateEvents(this.events);
}
});
this.calendarService.onEventsUpdated.subscribe(events => {
this.setEvents();
this.refresh.next();
});
}
setEvents()
{
this.events = this.calendarService.events.map(item => {
item.actions = this.actions;
return new CalendarEventModel(item);
});
}
/**
* Before View Renderer
* @param {any} header
* @param {any} body
*/
beforeMonthViewRender({header, body})
{
// console.info('beforeMonthViewRender');
/**
* Get the selected day
*/
const _selectedDay = body.find((_day) => {
return _day.date.getTime() === this.selectedDay.date.getTime();
});
if ( _selectedDay )
{
/**
* Set selectedday style
* @type {string}
*/
_selectedDay.cssClass = 'mat-elevation-z3';
}
}
/**
* Day clicked
* @param {MonthViewDay} day
*/
dayClicked(day: CalendarMonthViewDay): void
{
const date: Date = day.date;
const events: CalendarEvent[] = day.events;
if ( isSameMonth(date, this.viewDate) )
{
if ( (isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) || events.length === 0 )
{
this.activeDayIsOpen = false;
}
else
{
this.activeDayIsOpen = true;
this.viewDate = date;
}
}
this.selectedDay = day;
this.refresh.next();
}
/**
* Event times changed
* Event dropped or resized
* @param {CalendarEvent} event
* @param {Date} newStart
* @param {Date} newEnd
*/
eventTimesChanged({event, newStart, newEnd}: CalendarEventTimesChangedEvent): void
{
event.start = newStart;
event.end = newEnd;
// console.warn('Dropped or resized', event);
this.refresh.next(true);
}
/**
* Delete Event
* @param event
*/
deleteEvent(event)
{
this.confirmDialogRef = this.dialog.open(FuseConfirmDialogComponent, {
disableClose: false
});
this.confirmDialogRef.componentInstance.confirmMessage = 'Are you sure you want to delete?';
this.confirmDialogRef.afterClosed().subscribe(result => {
if ( result )
{
const eventIndex = this.events.indexOf(event);
this.events.splice(eventIndex, 1);
this.refresh.next(true);
}
this.confirmDialogRef = null;
});
}
/**
* Edit Event
* @param {string} action
* @param {CalendarEvent} event
*/
editEvent(action: string, event: CalendarEvent)
{
const eventIndex = this.events.indexOf(event);
this.dialogRef = this.dialog.open(FuseCalendarEventFormDialogComponent, {
panelClass: 'event-form-dialog',
data : {
event : event,
action: action
}
});
this.dialogRef.afterClosed()
.subscribe(response => {
if ( !response )
{
return;
}
const actionType: string = response[0];
const formData: FormGroup = response[1];
switch ( actionType )
{
/**
* Save
*/
case 'save':
this.events[eventIndex] = Object.assign(this.events[eventIndex], formData.getRawValue());
this.refresh.next(true);
break;
/**
* Delete
*/
case 'delete':
this.deleteEvent(event);
break;
}
});
}
/**
* Add Event
*/
addEvent(): void
{
this.dialogRef = this.dialog.open(FuseCalendarEventFormDialogComponent, {
panelClass: 'event-form-dialog',
data : {
action: 'new',
date : this.selectedDay.date
}
});
this.dialogRef.afterClosed()
.subscribe((response: FormGroup) => {
if ( !response )
{
return;
}
const newEvent = response.getRawValue();
newEvent.actions = this.actions;
this.events.push(newEvent);
this.refresh.next(true);
});
}
}

View File

@ -1,37 +0,0 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../../../../core/modules/shared.module';
import { RouterModule, Routes } from '@angular/router';
import { FuseCalendarComponent } from './calendar.component';
import { CalendarService } from './calendar.service';
import { CalendarModule } from 'angular-calendar';
import { FuseCalendarEventFormDialogComponent } from './event-form/event-form.component';
const routes: Routes = [
{
path : '**',
component: FuseCalendarComponent,
children : [],
resolve : {
chat: CalendarService
}
}
];
@NgModule({
imports : [
SharedModule,
RouterModule.forChild(routes),
CalendarModule.forRoot()
],
declarations : [
FuseCalendarComponent,
FuseCalendarEventFormDialogComponent
],
providers : [
CalendarService
],
entryComponents: [FuseCalendarEventFormDialogComponent]
})
export class FuseCalendarModule
{
}

View File

@ -1,58 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Http } from '@angular/http';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class CalendarService implements Resolve<any>
{
events: any;
onEventsUpdated = new Subject<any>();
constructor(private http: Http)
{
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
return new Promise((resolve, reject) => {
Promise.all([
this.getEvents()
]).then(
([events]: [any]) => {
resolve();
},
reject
);
});
}
getEvents()
{
return new Promise((resolve, reject) => {
this.http.get('api/calendar/events')
.subscribe(response => {
this.events = response.json().data.data;
this.onEventsUpdated.next(this.events);
resolve(this.events);
}, reject);
});
}
updateEvents(events)
{
return new Promise((resolve, reject) => {
this.http.post('api/calendar/events', {
id : 'events',
data: [...events]
})
.subscribe(response => {
this.getEvents();
}, reject);
});
}
}

View File

@ -1,141 +0,0 @@
<md-toolbar md-dialog-title class="mat-accent m-0">
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<span class="title dialog-title">{{dialogTitle}}</span>
<button md-button class="mat-icon-button"
(click)="dialogRef.close()"
aria-label="Close dialog">
<md-icon>close</md-icon>
</button>
</div>
</md-toolbar>
<div md-dialog-content class="p-24 m-0" perfect-scrollbar>
<form name="eventForm" [formGroup]="eventForm" class="event-form" fxLayout="column" fxFlex>
<md-input-container>
<input mdInput
name="title"
formControlName="title"
placeholder="Title"
required>
</md-input-container>
<div class="py-16" fxFlex="1 0 auto" fxLayout="row">
<md-slide-toggle
name="allDay"
formControlName="allDay"
class="mr-24"
aria-label="All day">
All Day
</md-slide-toggle>
</div>
<div class="py-16" fxFlex="1 0 auto" fxLayout="row" formGroupName="color">
<md-input-container class="mr-24">
<input mdInput
name="primary color"
formControlName="primary"
placeholder="Primary color"
[(colorPicker)]="event.color.primary"
cpWidth="290px"
[cpPresetColors]="presetColors"
[style.background]="event.color.primary"
(colorPickerChange)="event.color.primary = $event; eventForm.patchValue({color:{primary:$event}})"/>
</md-input-container>
<md-input-container>
<input mdInput
name="secondary color"
formControlName="secondary"
placeholder="Secondary color"
[(colorPicker)]="event.color.secondary"
cpWidth="290px"
[cpPresetColors]="presetColors"
[style.background]="event.color.secondary"
(colorPickerChange)="event.color.secondary = $event; eventForm.patchValue({color:{secondary:$event}})"/>
</md-input-container>
</div>
<div fxFlex="1 0 auto" fxLayout="row">
<md-input-container class="mr-24">
<input mdInput
name="start"
formControlName="start"
[mdDatepicker]="startDatePicker"
placeholder="Start Date">
<button mdSuffix [mdDatepickerToggle]="startDatePicker"></button>
</md-input-container>
<md-datepicker #startDatePicker></md-datepicker>
</div>
<div fxFlex="1 0 auto" fxLayout="row">
<md-input-container class="mr-24">
<input mdInput
name="end"
formControlName="end"
[mdDatepicker]="endDatePicker"
placeholder="End Date">
<button mdSuffix [mdDatepickerToggle]="endDatePicker"></button>
</md-input-container>
<md-datepicker #endDatePicker></md-datepicker>
<md-input-container class="no-errors-spacer" flex md-no-float>
<input mdInput ng-model="calendarEvent.endTime" placeholder="End Time">
</md-input-container>
</div>
<md-input-container formGroupName="meta">
<input mdInput
name="location"
formControlName="location"
placeholder="Location">
</md-input-container>
<md-input-container formGroupName="meta">
<textarea mdInput
formControlName="notes"
placeholder="Notes"
md-maxlength="250"
max-rows="4">
</textarea>
</md-input-container>
</form>
</div>
<div md-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="space-between center">
<button *ngIf="action !=='edit'"
md-raised-button
(click)="dialogRef.close(eventForm)"
class="save-button mat-accent"
[disabled]="eventForm.invalid"
aria-label="SAVE">
SAVE
</button>
<button *ngIf="action ==='edit'"
md-raised-button
(click)="dialogRef.close(['save',eventForm])"
class="save-button mat-accent"
[disabled]="eventForm.invalid"
aria-label="SAVE">
SAVE
</button>
<button *ngIf="action ==='edit'"
md-button
class="mat-icon-button"
(click)="dialogRef.close(['delete',eventForm])"
aria-label="Delete"
md-tooltip="Delete">
<md-icon>delete</md-icon>
</button>
</div>

View File

@ -1,12 +0,0 @@
.event-form-dialog {
.mat-dialog-container {
padding: 0;
width: 720px;
}
}
:host {
display: flex;
flex-direction: column;
}

View File

@ -1,71 +0,0 @@
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { MD_DIALOG_DATA, MdDialogRef } from '@angular/material';
import { CalendarEvent } from 'angular-calendar';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { CalendarEventModel } from '../event.model';
import { MatColors } from '../../../../../core/matColors';
import 'rxjs/Rx';
@Component({
selector : 'fuse-calendar-event-form-dialog',
templateUrl : './event-form.component.html',
styleUrls : ['./event-form.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseCalendarEventFormDialogComponent implements OnInit
{
event: CalendarEvent;
dialogTitle: string;
eventForm: FormGroup;
action: string;
presetColors = MatColors.presets;
constructor(
public dialogRef: MdDialogRef<FuseCalendarEventFormDialogComponent>,
@Inject(MD_DIALOG_DATA) private data: any,
private formBuilder: FormBuilder
)
{
this.event = data.event;
this.action = data.action;
if ( this.action === 'edit' )
{
this.dialogTitle = this.event.title;
}
else
{
this.dialogTitle = 'New Event';
this.event = new CalendarEventModel({
start: data.date,
end : data.date
});
}
this.eventForm = this.createEventForm();
}
ngOnInit()
{
}
createEventForm()
{
return new FormGroup({
title : new FormControl(this.event.title),
start : new FormControl(this.event.start),
end : new FormControl(this.event.end),
allDay: new FormControl(this.event.allDay),
color : this.formBuilder.group({
primary : new FormControl(this.event.color.primary),
secondary: new FormControl(this.event.color.secondary)
}),
meta :
this.formBuilder.group({
location: new FormControl(this.event.meta.location),
notes : new FormControl(this.event.meta.notes)
})
});
}
}

View File

@ -1,74 +0,0 @@
import {
CalendarEventAction
} from 'angular-calendar';
import {
startOfDay,
endOfDay,
subDays,
addDays,
endOfMonth,
isSameDay,
isSameMonth,
addHours
} from 'date-fns';
// import { CalendarEvent } from 'calendar-utils/dist/calendar-utils';
/*
export interface EventAction
{
label: string;
cssClass?: string;
onClick({event}: {
event: CalendarEvent;
}): any;
}*/
export class CalendarEventModel
{
start: Date;
end?: Date;
title: string;
color: {
primary: string;
secondary: string;
};
actions?: CalendarEventAction[];
allDay?: boolean;
cssClass?: string;
resizable?: {
beforeStart?: boolean;
afterEnd?: boolean;
};
draggable?: boolean;
meta?: {
location: string,
notes: string
};
constructor(data?)
{
data = data || {};
this.start = new Date(data.start) || startOfDay(new Date());
this.end = new Date(data.end) || endOfDay(new Date());
this.title = data.title || '';
this.color = {
primary : data.color && data.color.primary || '#1e90ff',
secondary: data.color && data.color.secondary || '#D1E8FF'
};
this.draggable = data.draggable || true;
this.resizable = {
beforeStart: data.resizable && data.resizable.beforeStart || true,
afterEnd : data.resizable && data.resizable.afterEnd || true
};
this.actions = data.actions || [];
this.allDay = data.allDay || false;
this.cssClass = data.cssClass || '';
this.meta = {
location: data.meta && data.meta.location || '',
notes : data.meta && data.meta.notes || ''
};
}
}

View File

@ -1,17 +0,0 @@
<div fxFlex fxLayout="column" fxLayoutAlign="center center">
<div class="big-circle mat-elevation-z1" fxLayout="column" fxLayoutAlign="center center">
<md-icon class="s-64 s-md-128">chat</md-icon>
</div>
<span class="app-title my-24">Chat App</span>
<span fxHide fxShow.gt-md class="app-message">Select contact to start the chat!..</span>
<button md-raised-button fxHide.gt-md class="" fuseMdSidenavToggler="chat-left-sidenav">
Select contact to start the chat!..
</button>
</div>

View File

@ -1,35 +0,0 @@
@import "src/app/core/scss/fuse";
:host {
display: flex;
flex: 1;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 20%, rgba(255, 255, 255, 0.8));
.big-circle {
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 20%, rgba(255, 255, 255, 0.8));
border-radius: 50%;
width: 300px;
height: 300px;
line-height: 300px;
text-align: center;
@include media-breakpoint-down('sm') {
width: 160px;
height: 160px;
line-height: 160px;
}
md-icon {
color: mat-color($accent);
}
}
.app-title {
font-weight: 500;
font-size: 32px;
}
.secondary-text {
font-size: 16px;
}
}

View File

@ -1,19 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector : 'fuse-chat-start',
templateUrl: './chat-start.component.html',
styleUrls : ['./chat-start.component.scss']
})
export class FuseChatStartComponent implements OnInit
{
constructor()
{
}
ngOnInit()
{
}
}

View File

@ -1,115 +0,0 @@
<!-- CHAT -->
<div class="chat" fxFlex fxLayout="column">
<!-- CHAT TOOLBAR -->
<md-toolbar class="chat-toolbar">
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<div fxLayout="row" fxLayoutAlign="start center">
<!-- RESPONSIVE CHATS BUTTON-->
<div md-button fxHide.gt-md class="responsive-chats-button mat-icon-button mr-16"
fuseMdSidenavToggler="chat-left-sidenav"
aria-label="chats button">
<md-icon class="s-36">chat</md-icon>
</div>
<!-- / RESPONSIVE CHATS BUTTON-->
<!-- CHAT CONTACT-->
<div class="chat-contact" fxLayout="row" fxLayoutAlign="start center"
fuseMdSidenavToggler="chat-right-sidenav" (click)="selectContact()">
<div class="avatar-wrapper">
<img [src]="contact.avatar"
class="avatar"
alt="{{contact.name}}"/>
<md-icon class="s-16 status"
[ngClass]="contact.status">
</md-icon>
</div>
<div class="chat-contact-name">
{{contact.name}}
</div>
</div>
<!-- / CHAT CONTACT-->
</div>
<div>
<button md-button class="mat-icon-button" [mdMenuTriggerFor]="contactMenu"
aria-label="more">
<md-icon>more_vert</md-icon>
</button>
<md-menu #contactMenu="mdMenu">
<button md-menu-item fuseMdSidenavToggler="chat-right-sidenav" (click)="selectContact()">
Contact Info
</button>
</md-menu>
</div>
</div>
</md-toolbar>
<!-- / CHAT TOOLBAR -->
<!-- CHAT CONTENT -->
<div id="chat-content" fxFlex perfect-scrollbar>
<!-- CHAT MESSAGES -->
<div class="chat-messages">
<!-- MESSAGE -->
<div fxLayout="row" *ngFor="let message of dialog" class="message-row"
[ngClass]="{'user' :message.who === user.id}">
<img *ngIf="message.who === contact.id"
src="{{contact.avatar}}"
class="avatar"
alt="{{contact.name}}"/>
<img *ngIf="message.who ===user.id" class="avatar" src="{{user.avatar}}">
<div class="bubble">
<div class="message">{{message.message}}</div>
<div class="time secondary-text">{{message.time | date:'medium'}}</div>
</div>
</div>
<!-- / MESSAGE -->
</div>
<!-- CHAT MESSAGES -->
</div>
<!-- / CHAT CONTENT -->
<!-- CHAT FOOTER -->
<div class="chat-footer" fxLayout="row" fxLayoutAlign="center center">
<!-- REPLY FORM -->
<form #replyForm="ngForm"
(ngSubmit)="reply($event)"
(keyup.enter)="reply($event)"
fxFlex class="reply-form"
fxLayout="row"
fxLayoutAlign="start center">
<md-input-container class="" fxFlex floatPlaceholder="never">
<textarea mdInput #replyInput placeholder="Type and hit enter to send message"
ngModel name="message"></textarea>
</md-input-container>
<button md-fab class="" type="submit" aria-label="Send message">
<md-icon>send</md-icon>
</button>
</form>
<!-- / REPLY FORM -->
</div>
<!-- / CHAT FOOTER-->
</div>
<!-- / CHAT -->

View File

@ -1,133 +0,0 @@
:host {
display: flex;
flex: 1 0 auto;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 20%, rgba(255, 255, 255, 0.8));
overflow: hidden;
.chat {
.chat-toolbar {
min-height: 64px;
background-color: #F3F4F5;
color: rgba(0, 0, 0, 0.87);
border-bottom: 1px solid rgba(0, 0, 0, .08);
.responsive-chats-button {
padding: 0;
}
.chat-contact {
cursor: pointer;
.avatar {
margin-right: 16px;
}
.chat-contact-name {
}
}
}
#chat-content {
background: transparent;
.message-row {
padding: 16px;
.bubble {
position: relative;
padding: 6px 7px 8px 9px;
background-color: #FFF;
box-shadow: 0 1px .5px rgba(0, 0, 0, .13);
border-radius: 6px;
&:before {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAMAAADp2asXAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADGUExURQAAAP////b29vn5+f///wAAAP///wAAAAAAAP///9ra2v////j4+PHx8fv7++Hh4fHx8f////////////////39/QAAAP////////z8/P////39/f39/fz8/P////////////z8/P////////////z8/P////////////v7+/Hx8f///9bW1vz8/K2trf////39/f39/WJiYgAAAExMTFtbWwAAAN3d3cjIyPr6+vX19QAAAO7u7vz8/NTU1Ofn5zMzM////zGPlXsAAABBdFJOUwAcm/kREh4CCDWL1SneR6TfAQffhMYK/A5nRrLWfRc5DW2ih5f+19Kn+9v4g/1LCJuXHwQUKgahcXS6DNnlDMMKKzPoTgAAAKBJREFUKM+V08USwmAQA+C/0NIWd3d3d8/7vxTMcIPkQK7f7CG7s8bQAOY/SCuwFYQU1P+eiCqIK2gpWCmoCrAgoKQgJ8CHgIqAMjg0MxxSQ3DogEMWFBZtUPAHYGB1CyDQWE6AH7BrfXzlAxGAQhECTGAmwN1Okz0Gb/LW4fEItIfrOfNELMh3tck7u+PhcT2zQ7l77/K8iY8yJwV3BeYFqpc/uSyPGdAAAAAASUVORK5CYII=);
content: '';
position: absolute;
left: -11px;
bottom: 3px;
width: 12px;
height: 19px;
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: contain;
}
.message {
white-space: pre-wrap;
}
.time {
font-size: 11px;
margin-top: 8px;
text-align: right;
}
}
&.contact {
.avatar {
margin: 0 16px 0 0;
}
}
&.user {
align-items: flex-end;
.avatar {
order: 2;
margin: 0 0 0 16px;
}
.bubble {
margin-left: auto;
background-color: #E8F5E9;
border: 1px solid #DFEBE0;
order: 1;
&:before {
right: -11px;
left: auto;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAMAAADp2asXAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD2UExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRsXAAAANzwzNPmxNrtyau5oIWRedDkwNntyczgwdfpyJ+/n97wzsLWtNjsytvwzczfvtPmxau6nNjqxtrtyio1KtzwzNjryAAAANzwzgAAANzwzK7Aor/Us9Lnw8vevAAAAMzevtbpxrvMrX+IdwAAAEROOi45Lr3MrZGjf9LoxX+MctnqydLkwhgYGMzfv9vuyQAAANzwzNvuy9zxy7vMu7XGqNvtzKKykwAAANruzKq6nLnMriQkGMXXuL3PsNjsySgzKAAAANLkw83fvd3vy9z4xtzwzRpFmIEAAABQdFJOUwAXChEGBAMBAgwhDvJ7k0YqMc0Zmwj6apf2kjU0+dkw/swh/CP9j2Wr2gndvaYeBRoxQg6gUPt/FaHJGdTj9A9k7XQLeE6iFcN12xkSt9r4NKizowAAAMFJREFUKM+V0sdywlAMBVDbMX7PQCihQ+iQ0HsJvfem/P/PwBIzugu0PXNnNNJVyPmhsIPhhoB2COwIGuLdhAcl3AhCBoBoHUC6BCBbA0C/EkBFB5D/FjxQwQYg1RI8UKINgDoSAPUlAPqUAMgfAEBfXsEDBV0+Hogi4Zhg4THj9YwHoqEBYOrgYTI3GVgMNn8r+Qq94k9yZNosW/3Hy9VuTjWfHkOX6367bGZUU7de66ieHZrO1OGg8Z1WTgYAFLgD5S1PCkzo1B0AAAAASUVORK5CYII=);
}
}
}
}
}
.chat-footer {
min-height: 64px;
max-height: 96px;
background-color: #F3F4F5;
color: rgba(0, 0, 0, 0.87);
border-top: 1px solid rgba(0, 0, 0, .08);
padding: 8px 8px 8px 16px;
.reply-form {
md-input-container {
margin: 0;
padding-right: 16px;
textarea {
overflow: auto;
max-height: 80px;
transition: height 200ms ease;
&.grow {
height: 80px;
}
}
.md-errors-spacer {
display: none;
}
}
.md-button {
margin: 0;
}
}
}
}
}

View File

@ -1,100 +0,0 @@
import { AfterViewInit, Component, OnInit, ViewChild, ViewChildren } from '@angular/core';
import { ChatService } from '../chat.service';
import { NgForm } from '@angular/forms';
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
@Component({
selector : 'fuse-chat-view',
templateUrl: './chat-view.component.html',
styleUrls : ['./chat-view.component.scss']
})
export class FuseChatViewComponent implements OnInit, AfterViewInit
{
user: any;
chat: any;
dialog: any;
contact: any;
replyInput: any;
selectedChat: any;
@ViewChild(PerfectScrollbarDirective) directiveScroll: PerfectScrollbarDirective;
@ViewChildren('replyInput') replyInputField;
@ViewChild('replyForm') replyForm: NgForm;
constructor(private chatService: ChatService)
{
}
ngOnInit()
{
this.user = this.chatService.user;
this.chatService.onChatSelected
.subscribe(chatData => {
if ( chatData )
{
this.selectedChat = chatData;
this.contact = chatData.contact;
this.dialog = chatData.dialog;
this.readyToReply();
}
});
}
ngAfterViewInit()
{
this.replyInput = this.replyInputField.first.nativeElement;
this.readyToReply();
}
selectContact()
{
this.chatService.selectContact(this.contact);
}
readyToReply()
{
setTimeout(() => {
this.replyForm.reset();
this.focusReplyInput();
this.scrollToBottom();
});
}
focusReplyInput()
{
setTimeout(() => {
this.replyInput.focus();
});
}
scrollToBottom(speed?: number)
{
speed = speed || 400;
if ( this.directiveScroll )
{
setTimeout(() => {
this.directiveScroll.scrollToBottom(0, speed);
});
}
}
reply(event)
{
// Message
const message = {
who : this.user.id,
message: this.replyForm.form.value.message,
time : new Date().toISOString()
};
// Add the message to the chat
this.dialog.push(message);
// Update the server
this.chatService.updateDialog(this.selectedChat.chatId, this.dialog).then(response => {
this.readyToReply();
});
}
}

View File

@ -1,43 +0,0 @@
<div id="calendar" class="page-layout carded fullwidth">
<!-- TOP BACKGROUND -->
<div class="top-bg md-accent-bg"></div>
<!-- / TOP BACKGROUND -->
<!-- CENTER -->
<div class="center">
<!-- CONTENT CARD -->
<div class="content-card">
<md-sidenav-container>
<!-- LEFT SIDENAV -->
<md-sidenav class="sidenav mat-sidenav-opened" align="start" opened="true" mode="side"
fuseMdSidenavHelper="chat-left-sidenav" md-is-locked-open="gt-md">
<fuse-chat-left-sidenav></fuse-chat-left-sidenav>
</md-sidenav>
<!-- / LEFT SIDENAV -->
<!-- CONTENT -->
<fuse-chat-start *ngIf="!selectedChat"></fuse-chat-start>
<fuse-chat-view *ngIf="selectedChat"></fuse-chat-view>
<!-- / CONTENT -->
<!-- RIGHT SIDENAV -->
<md-sidenav class="sidenav mat-sidenav-opened" align="end" opened="false" mode="over"
fuseMdSidenavHelper="chat-right-sidenav">
<fuse-chat-right-sidenav></fuse-chat-right-sidenav>
</md-sidenav>
<!-- / RIGHT SIDENAV -->
</md-sidenav-container>
</div>
<!-- / CONTENT CARD -->
</div>
<!-- / CENTER -->
</div>

View File

@ -1,40 +0,0 @@
#calendar {
display: flex;
flex: 1;
.center {
padding: 32px !important;
max-width: 1400px;
margin: 0 auto;
.content-card {
display: flex;
flex: 1;
position: relative;
background: url('/assets/images/patterns/rain-grey.png') repeat;
.mat-sidenav-container {
display: flex;
flex: 1;
width: 100%;
background: transparent;
.mat-sidenav-content {
display: flex;
flex: 1 0 auto;
min-height: 100%;
height: auto;
}
md-sidenav {
display: flex;
flex-direction: column;
width: 400px;
max-width: 90%;
box-shadow: 0 0 1px rgba(0, 0, 0, .37);
overflow: hidden;
}
}
}
}
}

View File

@ -1,26 +0,0 @@
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ChatService } from './chat.service';
@Component({
selector : 'fuse-chat',
templateUrl : './chat.component.html',
styleUrls : ['./chat.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseChatComponent implements OnInit
{
selectedChat: any;
constructor(private chatService: ChatService)
{
}
ngOnInit()
{
this.chatService.onChatSelected
.subscribe(chatData => {
this.selectedChat = chatData;
});
}
}

View File

@ -1,46 +0,0 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../../../../core/modules/shared.module';
import { RouterModule, Routes } from '@angular/router';
import { FuseChatComponent } from './chat.component';
import { ChatService } from './chat.service';
import { FuseChatViewComponent } from './chat-view/chat-view.component';
import { FuseChatStartComponent } from './chat-start/chat-start.component';
import { FuseChatChatsSidenavComponent } from './sidenavs/left/chats/chats.component';
import { FuseChatUserSidenavComponent } from './sidenavs/left/user/user.component';
import { FuseChatLeftSidenavComponent } from './sidenavs/left/left.component';
import { FuseChatRightSidenavComponent } from './sidenavs/right/right.component';
import { FuseChatContactSidenavComponent } from './sidenavs/right/contact/contact.component';
const routes: Routes = [
{
path : '**',
component: FuseChatComponent,
children : [],
resolve : {
chat: ChatService
}
}
];
@NgModule({
imports : [
SharedModule,
RouterModule.forChild(routes)
],
declarations: [
FuseChatComponent,
FuseChatViewComponent,
FuseChatStartComponent,
FuseChatChatsSidenavComponent,
FuseChatUserSidenavComponent,
FuseChatLeftSidenavComponent,
FuseChatRightSidenavComponent,
FuseChatContactSidenavComponent
],
providers : [
ChatService
]
})
export class FuseChatModule
{
}

View File

@ -1,247 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Http } from '@angular/http';
import { Subject } from 'rxjs/Subject';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { FuseUtils } from '../../../../core/fuseUtils';
@Injectable()
export class ChatService implements Resolve<any>
{
contacts: any[];
chats: any[];
user: any;
onChatSelected = new BehaviorSubject<any>(null);
onContactSelected = new BehaviorSubject<any>(null);
onChatsUpdated = new Subject<any>();
onUserUpdated = new Subject<any>();
onLeftSidenavViewChanged = new Subject<any>();
onRightSidenavViewChanged = new Subject<any>();
constructor(private http: Http)
{
}
/**
* Get chat
* @param contactId
* @returns {Promise<any>}
*/
getChat(contactId)
{
const chatItem = this.user.chatList.find((item) => {
return item.contactId === contactId;
});
/**
* Create new chat, if it's not created yet.
*/
if ( !chatItem )
{
this.createNewChat(contactId).then((newChats) => {
this.getChat(contactId);
});
return;
}
return new Promise((resolve, reject) => {
this.http.get('api/chat-chats/' + chatItem.id)
.subscribe(response => {
const chat = response.json().data;
const chatContact = this.contacts.find((contact) => {
return contact.id === contactId;
});
const chatData = {
chatId : chat.id,
dialog : chat.dialog,
contact: chatContact
};
this.onChatSelected.next({...chatData});
}, reject);
});
}
/**
* Create New Chat
* @param contactId
* @returns {Promise<any>}
*/
createNewChat(contactId)
{
return new Promise((resolve, reject) => {
const contact = this.contacts.find((item) => {
return item.id === contactId;
});
const chatId = FuseUtils.generateGUID();
const chat = {
id : chatId,
dialog: []
};
const chatListItem = {
contactId : contactId,
id : chatId,
lastMessageTime: '2017-02-18T10:30:18.931Z',
name : contact.name,
unread : null
};
/**
* Add new chat list item to the user's chat list
*/
this.user.chatList.push(chatListItem);
/**
* Post the created chat
*/
this.http.post('api/chat-chats', {...chat})
.subscribe(response => {
/**
* Post the new the user data
*/
this.http.post('api/chat-user/' + this.user.id, this.user)
.subscribe(newUserData => {
/**
* Update the user data from server
*/
this.getUser().then(updatedUser => {
this.onUserUpdated.next(updatedUser);
resolve(updatedUser);
});
});
}, reject);
});
}
/**
* Select Contact
* @param contact
*/
selectContact(contact)
{
this.onContactSelected.next(contact);
}
/**
* Set user status
* @param status
*/
setUserStatus(status)
{
this.user.status = status;
}
/**
* Update user data
* @param userData
*/
updateUserData(userData)
{
this.http.post('api/chat-user/' + this.user.id, userData)
.subscribe(response => {
this.user = userData;
}
);
}
/**
* Update the chat dialog
* @param chatId
* @param dialog
* @returns {Promise<any>}
*/
updateDialog(chatId, dialog): Promise<any>
{
return new Promise((resolve, reject) => {
const newData = {
id : chatId,
dialog: dialog
};
this.http.post('api/chat-chats/' + chatId, newData)
.subscribe(updatedChat => {
resolve(updatedChat);
}, reject);
});
}
/**
* The Chat App Main Resolver
* @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any}
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
return new Promise((resolve, reject) => {
Promise.all([
this.getContacts(),
this.getChats(),
this.getUser()
]).then(
([contacts, chats, user]) => {
this.contacts = contacts;
this.chats = chats;
this.user = user;
resolve();
},
reject
);
});
}
/**
* Get Contacts
* @returns {Promise<any>}
*/
getContacts(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/chat-contacts')
.subscribe(response => {
resolve(response.json().data);
}, reject);
});
}
/**
* Get Chats
* @returns {Promise<any>}
*/
getChats(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/chat-chats')
.subscribe(response => {
resolve(response.json().data);
}, reject);
});
}
/**
* Get User
* @returns {Promise<any>}
*/
getUser(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/chat-user')
.subscribe(response => {
resolve(response.json().data[0]);
}, reject);
});
}
}

View File

@ -1,182 +0,0 @@
<!-- SIDENAV HEADER -->
<div class="sidenav-header">
<!-- CHATS TOOLBAR -->
<md-toolbar>
<!-- TOOLBAR TOP -->
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<!-- USER AVATAR WRAPPER -->
<div class="avatar-wrapper">
<!-- USER AVATAR -->
<img (click)="changeLeftSidenavView('user')"
src="{{user.avatar}}"
class="md-avatar avatar"
alt="{{user.name}}"/>
<!-- / USER AVATAR -->
<md-icon class="s-16 status" [ngClass]="user.status" [mdMenuTriggerFor]="userStatusMenu"></md-icon>
<!-- USER STATUS -->
<md-menu id="user-status-menu" #userStatusMenu="mdMenu">
<button md-menu-item (click)="setUserStatus('online')">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-16 status online"></md-icon>
<span>Online</span>
</div>
</button>
<button md-menu-item (click)="setUserStatus('away')">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-16 status away"></md-icon>
<span>Away</span>
</div>
</button>
<button md-menu-item (click)="setUserStatus('do-not-disturb')">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-16 status do-not-disturb"></md-icon>
<span>Do not disturb</span>
</div>
</button>
<button md-menu-item (click)="setUserStatus('offline')">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-16 status offline"></md-icon>
<span>Offline</span>
</div>
</button>
</md-menu>
<!-- / USER STATUS -->
</div>
<!-- / USER AVATAR -->
<div>
<button md-button class="mat-icon-button"
[mdMenuTriggerFor]="userMenu"
aria-label="more">
<md-icon>more_vert</md-icon>
</button>
<md-menu #userMenu="mdMenu">
<button md-menu-item (click)="changeLeftSidenavView('user')">
Profile
</button>
<button md-menu-item (click)="logout()">
Logout
</button>
</md-menu>
</div>
</div>
<!-- / TOOLBAR TOP -->
<!-- TOOLBAR BOTTOM -->
<md-toolbar-row>
<!-- SEARCH -->
<div class="search-wrapper" fxFlex fxLayout="row" fxLayoutAlign="start center">
<div class="search" fxFlex fxLayout="row" fxLayoutAlign="start center">
<md-icon>search</md-icon>
<input [(ngModel)]="searchText" type="text" placeholder="Search or start new chat" fxFlex>
</div>
</div>
<!-- / SEARCH -->
</md-toolbar-row>
<!-- / TOOLBAR BOTTOM -->
</md-toolbar>
<!-- / CHATS TOOLBAR -->
</div>
<!-- / SIDENAV HEADER -->
<!-- SIDENAV CONTENT -->
<div class="sidenav-content" perfect-scrollbar fxFlex>
<!-- CHATS CONTENT -->
<div>
<!-- CHATS LIST-->
<div class="chat-list" fxLayout="column">
<div md-subheader *ngIf="(user.chatList | filter: searchText).length > 0">
Chats
</div>
<button md-button class="contact"
*ngFor="let chat of user.chatList | filter: searchText"
(click)="getChat(chat.contactId)" ngClass="{'unread':contact.unread}">
<div fxLayout="row" fxLayoutAlign="start center">
<div class="avatar-wrapper" fxFlex="0 1 auto" fxLayoutAlign="center center">
<img [src]="contacts |getById:chat.contactId:'avatar'"
class="avatar"
alt="{{contacts |getById:chat.contactId:'name'}}"/>
<md-icon class="s-16 status" [ngClass]="contacts |getById:chat.contactId:'status'"></md-icon>
</div>
<div fxLayout="row" fxFlex>
<div class="" fxFlex fxLayout="column" fxLayoutAlign="center start">
<div class="contact-name">{{contacts |getById:chat.contactId:'name'}}</div>
</div>
<div fxLayout="column" fxLayoutAlign="center end">
<div class="contact-last-message-time">
{{chat.lastMessageTime | date}}
</div>
<div *ngIf="chat.unread" class="unread-message-count">{{chat.unread}}</div>
</div>
</div>
</div>
</button>
</div>
<!-- / CHATS LIST-->
<!-- CONTACTS LIST-->
<div class="contact-list" fxLayout="column">
<div md-subheader *ngIf="(contacts| filter: searchText).length > 0">
Contacts
</div>
<button md-button class="contact"
ng-show="chatSearch"
*ngFor="let contact of contacts| filter: searchText"
(click)="getChat(contact.id)">
<div fxLayout="row" fxLayoutAlign="start center">
<div class="avatar-wrapper" fxFlex="0 1 auto">
<img src="{{contact.avatar}}" class="md-avatar avatar" alt="{{contact.name}}"/>
<md-icon class="s-16 status" [ngClass]="contact.status"></md-icon>
</div>
<div class="" fxLayout="column" fxLayoutAlign="center start">
<div class="contact-name">{{contact.name}}</div>
<p class="contact-mood">{{contact.mood}}</p>
</div>
</div>
</button>
</div>
<!-- / CONTACTS LIST-->
<!-- NO RESULTS MESSAGE -->
<div *ngIf="(contacts| filter: searchText).length === 0" class="no-results-message">
No results..
</div>
<!-- NO RESULTS MESSAGE-->
</div>
<!-- / CHATS CONTENT -->
</div>
<!-- / SIDENAV CONTENT -->

View File

@ -1,113 +0,0 @@
@import "src/app/core/scss/fuse";
:host {
display: flex;
flex: 1;
flex-direction: column;
.sidenav-header {
md-toolbar {
border-bottom: 1px solid rgba(0, 0, 0, .08);
.avatar-wrapper {
.avatar, .status {
cursor: pointer;
}
}
.search {
height: 36px;
line-height: 36px;
padding: 8px;
background: #FFFFFF;
font-size: 13px;
@include mat-elevation(1);
.icon {
margin: 0;
color: rgba(0, 0, 0, 0.54);
}
input {
padding-left: 12px;
height: 36px;
color: rgba(0, 0, 0, 0.54);
border: none;
}
}
}
}
.sidenav-content {
overflow: auto;
.contact-list, .chat-list {
.mat-subheader {
padding-left: 16px;
font-size: 20px;
font-weight: 300;
height: 88px;
line-height: 88px;
color: mat-color($accent);
}
.contact {
white-space: normal;
text-align: left;
letter-spacing: .010em;
min-height: 88px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
padding: 16px;
font-weight: 400;
.avatar-wrapper {
.avatar {
margin-right: 16px;
}
}
.contact-name {
font-size: 16px;
white-space: nowrap;
text-overflow: ellipsis;
}
.contact-last-message {
line-height: 1.6em;
margin: 0;
font-weight: 500;
color: rgba(0, 0, 0, 0.54);
}
.contact-mood {
line-height: normal;
margin-top: 0;
}
.unread-message-count {
border-radius: 50%;
text-align: center;
width: 24px;
height: 24px;
line-height: 24px;
background-color: mat-color($accent);
color: map-get($accent, default-contrast);
}
}
}
.no-results-message {
position: absolute;
width: 100%;
height: 88px;
padding: 16px;
background: #FFFFFF;
font-size: 15px;
font-weight: 400;
}
}
}

View File

@ -1,69 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ChatService } from '../../../chat.service';
import { FuseMdSidenavHelperService } from '../../../../../../../core/directives/md-sidenav-helper/md-sidenav-helper.service';
import { ObservableMedia } from '@angular/flex-layout';
@Component({
selector : 'fuse-chat-chats-sidenav',
templateUrl: './chats.component.html',
styleUrls : ['./chats.component.scss']
})
export class FuseChatChatsSidenavComponent implements OnInit
{
user: any;
chats: any[];
contacts: any[];
chatSearch: any;
searchText = '';
constructor(
private chatService: ChatService,
private fuseMdSidenavService: FuseMdSidenavHelperService,
public media: ObservableMedia
)
{
this.chatSearch = {
name: ''
};
}
ngOnInit()
{
this.user = this.chatService.user;
this.chats = this.chatService.chats;
this.contacts = this.chatService.contacts;
this.chatService.onChatsUpdated.subscribe(updatedChats => {
this.chats = updatedChats;
});
this.chatService.onUserUpdated.subscribe(updatedUser => {
this.user = updatedUser;
});
}
getChat(contact)
{
this.chatService.getChat(contact);
if ( !this.media.isActive('gt-md') )
{
this.fuseMdSidenavService.getSidenav('chat-left-sidenav').toggle();
}
}
setUserStatus(status)
{
this.chatService.setUserStatus(status);
}
changeLeftSidenavView(view)
{
this.chatService.onLeftSidenavViewChanged.next(view);
}
logout()
{
console.log('logout triggered');
}
}

View File

@ -1,11 +0,0 @@
<div [ngSwitch]="view" class="views">
<fuse-chat-chats-sidenav class="view"
*ngSwitchCase="'chats'"
[@slideInRight]>
</fuse-chat-chats-sidenav>
<fuse-chat-user-sidenav class="view"
*ngSwitchCase="'user'"
[@slideInLeft]>
</fuse-chat-user-sidenav>
</div>

View File

@ -1,20 +0,0 @@
:host {
display: flex;
flex-direction: column;
height: 100%;
.views {
display: flex;
flex-direction: column;
height: 100%;
.view {
position: absolute;
height: 100%;
bottom: 0;
left: 0;
right: 0;
top: 0;
}
}
}

View File

@ -1,27 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { Animations } from '../../../../../../core/animations';
import { ChatService } from '../../chat.service';
@Component({
selector : 'fuse-chat-left-sidenav',
templateUrl: './left.component.html',
styleUrls : ['./left.component.scss'],
animations : [Animations.slideInLeft, Animations.slideInRight]
})
export class FuseChatLeftSidenavComponent implements OnInit
{
view: string;
constructor(private chatService: ChatService)
{
this.view = 'chats';
}
ngOnInit()
{
this.chatService.onLeftSidenavViewChanged.subscribe(view => {
this.view = view;
});
}
}

View File

@ -1,78 +0,0 @@
<!-- SIDENAV HEADER -->
<div class="sidenav-header">
<!-- USER TOOLBAR -->
<md-toolbar>
<!-- TOOLBAR TOP -->
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<button md-button class="mat-icon-button" (click)="changeLeftSidenavView('chats')" aria-label="back">
<md-icon>arrow_back</md-icon>
</button>
</div>
<!-- / TOOLBAR TOP -->
<!-- TOOLBAR BOTTOM -->
<md-toolbar-row class="toolbar-bottom" fxLayout="column" fxLayoutAlign="center center">
<img [src]="user.avatar" class="avatar user-avatar huge" alt="{{user.name}}"/>
<div class="user-name my-8">{{user.name}}</div>
</md-toolbar-row>
<!-- / TOOLBAR BOTTOM -->
</md-toolbar>
<!-- / USER TOOLBAR -->
</div>
<!-- SIDENAV CONTENT -->
<div class="sidenav-content p-16" perfect-scrollbar fxFlex>
<!-- USER MOOD -->
<md-card>
<form [formGroup]="userForm" fxLayout="column">
<md-input-container class="mb-24" fxFlex="0 1 auto">
<textarea mdInput placeholder="Mood" name="mood"
formControlName="mood" rows="3"></textarea>
</md-input-container>
<md-radio-group formControlName="status" fxLayout="column">
<md-radio-button value="online" class="py-8">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon class="status online mr-8"></md-icon>
<span class="mat-h4 m-0">Online</span>
</div>
</md-radio-button>
<md-radio-button value="away" class="py-8">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon class="status away mr-8"></md-icon>
<span class="mat-h4 m-0">Away</span>
</div>
</md-radio-button>
<md-radio-button value="do-not-disturb" class="py-8">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon class="status do-not-disturb mr-8"></md-icon>
<span class="mat-h4 m-0">Do not disturb</span>
</div>
</md-radio-button>
<md-radio-button value="offline" class="py-8">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon class="status offline mr-8"></md-icon>
<span class="mat-h4 m-0">Offline</span>
</div>
</md-radio-button>
</md-radio-group>
</form>
</md-card>
<!-- / USER MOOD -->
</div>

View File

@ -1,21 +0,0 @@
@import "src/app/core/scss/fuse";
:host {
display: flex;
flex: 1;
flex-direction: column;
md-toolbar {
background-color: mat-color($accent);
color: map-get($accent, default-contrast);
.toolbar-bottom {
height: 240px;
}
}
.sidenav-content {
background: whitesmoke;
}
}

View File

@ -1,47 +0,0 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ChatService } from '../../../chat.service';
import { FormControl, FormGroup } from '@angular/forms';
import 'rxjs/Rx';
@Component({
selector : 'fuse-chat-user-sidenav',
templateUrl: './user.component.html',
styleUrls : ['./user.component.scss']
})
export class FuseChatUserSidenavComponent implements OnInit, OnDestroy
{
user: any;
onFormChange: any;
userForm: FormGroup;
constructor(private chatService: ChatService)
{
this.user = this.chatService.user;
this.userForm = new FormGroup({
mood : new FormControl(this.user.mood),
status: new FormControl(this.user.status)
});
}
ngOnInit()
{
this.onFormChange = this.userForm.valueChanges
.debounceTime(500)
.distinctUntilChanged()
.subscribe(data => {
this.user.mood = data.mood;
this.user.status = data.status;
this.chatService.updateUserData(this.user);
});
}
changeLeftSidenavView(view)
{
this.chatService.onLeftSidenavViewChanged.next(view);
}
ngOnDestroy()
{
this.onFormChange.unsubscribe();
}
}

View File

@ -1,47 +0,0 @@
<!-- SIDENAV HEADER -->
<div class="sidenav-header" *ngIf="contact">
<!-- CONTACT TOOLBAR -->
<md-toolbar>
<!-- TOOLBAR TOP -->
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<div>Contact Info</div>
<button md-button class="mat-icon-button" fuseMdSidenavToggler="chat-right-sidenav" aria-label="close">
<md-icon>close</md-icon>
</button>
</div>
<!-- / TOOLBAR TOP -->
<!-- TOOLBAR BOTTOM -->
<md-toolbar-row class="toolbar-bottom" fxLayout="column" fxLayoutAlign="center center">
<img [src]="contact.avatar" class="avatar contact-avatar huge" alt="{{contact.name}}"/>
<div class="contact-name my-8">{{contact.name}}</div>
</md-toolbar-row>
<!-- / TOOLBAR BOTTOM -->
</md-toolbar>
<!-- / CONTACT TOOLBAR -->
</div>
<!-- SIDENAV CONTENT -->
<div class="sidenav-content p-16" perfect-scrollbar fxFlex *ngIf="contact">
<!-- CONTACT MOOD -->
<md-card>
<md-input-container fxFlex>
<textarea mdInput placeholder="Mood" name="mood"
[value]="contact.mood" rows="3" disabled>
</textarea>
</md-input-container>
</md-card>
<!-- / CONTACT MOOD -->
</div>

View File

@ -1,21 +0,0 @@
@import "src/app/core/scss/fuse";
:host {
display: flex;
flex: 1;
flex-direction: column;
md-toolbar {
background-color: mat-color($accent);
color: map-get($accent, default-contrast);
.toolbar-bottom {
height: 240px;
}
}
.sidenav-content {
background: whitesmoke;
}
}

View File

@ -1,25 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ChatService } from '../../../chat.service';
@Component({
selector : 'fuse-chat-contact-sidenav',
templateUrl: './contact.component.html',
styleUrls : ['./contact.component.scss']
})
export class FuseChatContactSidenavComponent implements OnInit
{
contact: any;
constructor(private chatService: ChatService)
{
}
ngOnInit()
{
this.chatService.onContactSelected.subscribe(contact => {
this.contact = contact;
});
}
}

View File

@ -1,8 +0,0 @@
<div [ngSwitch]="view" class="views">
<fuse-chat-contact-sidenav class="view"
*ngSwitchCase="'contact'"
[@slideInRight]>
</fuse-chat-contact-sidenav>
</div>

View File

@ -1,20 +0,0 @@
:host {
display: flex;
flex-direction: column;
height: 100%;
.views {
display: flex;
flex-direction: column;
height: 100%;
.view {
position: absolute;
height: 100%;
bottom: 0;
left: 0;
right: 0;
top: 0;
}
}
}

View File

@ -1,27 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { Animations } from '../../../../../../core/animations';
import { ChatService } from '../../chat.service';
@Component({
selector : 'fuse-chat-right-sidenav',
templateUrl: './right.component.html',
styleUrls : ['./right.component.scss'],
animations : [Animations.slideInLeft, Animations.slideInRight]
})
export class FuseChatRightSidenavComponent implements OnInit
{
view: string;
constructor(private chatService: ChatService)
{
this.view = 'contact';
}
ngOnInit()
{
this.chatService.onRightSidenavViewChanged.subscribe(view => {
this.view = view;
});
}
}

View File

@ -1,129 +0,0 @@
<md-toolbar md-dialog-title class="mat-accent m-0">
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<span class="title dialog-title">{{dialogTitle}}</span>
<button md-button class="mat-icon-button"
(click)="dialogRef.close()"
aria-label="Close dialog">
<md-icon>close</md-icon>
</button>
</div>
<md-toolbar-row class="toolbar-bottom" fxLayout="column" fxLayoutAlign="center center">
<img [src]="contact.avatar" class=" avatar contact-avatar huge"
[alt]="contact.name"/>
<div class="contact-name">{{contact.name}}</div>
</md-toolbar-row>
</md-toolbar>
<div md-dialog-content class="p-24 m-0" perfect-scrollbar>
<form [formGroup]="contactForm">
<div class="mb-24" fxLayout="row" fxLayoutAlign="start start">
<md-icon class="mr-12 mt-12">account_circle</md-icon>
<md-input-container fxFlex>
<input name="name" formControlName="name" placeholder="Name" mdInput required>
</md-input-container>
</div>
<div class="mb-24" fxLayout="row" fxLayoutAlign="start start">
<md-icon class="mr-12 mt-12"></md-icon>
<md-input-container fxFlex>
<input name="lastName" formControlName="lastName" placeholder="Lastname" mdInput>
</md-input-container>
</div>
<div class="mb-24" fxLayout="row" fxLayoutAlign="start start">
<md-icon class="mr-12 mt-12">star</md-icon>
<md-input-container fxFlex>
<input name="nickname" formControlName="nickname" mdInput placeholder="Nickname">
</md-input-container>
</div>
<div class="mb-24" fxLayout="row" fxLayoutAlign="start start">
<md-icon class="mr-12 mt-12">phone</md-icon>
<md-input-container fxFlex>
<input formControlName="phone" mdInput placeholder="Phone number">
</md-input-container>
</div>
<div class="mb-24" fxLayout="row" fxLayoutAlign="start start">
<md-icon class="mr-12 mt-12">email</md-icon>
<md-input-container fxFlex>
<input name="email" formControlName="email" mdInput type="email" placeholder="Email">
</md-input-container>
</div>
<div class="mb-24" fxLayout="row" fxLayoutAlign="start start">
<md-icon class="mr-12 mt-12">domain</md-icon>
<md-input-container fxFlex>
<input name="company" formControlName="company" mdInput placeholder="Company">
</md-input-container>
</div>
<div class="mb-24" fxLayout="row" fxLayoutAlign="start start">
<md-icon class="mr-12 mt-12">work</md-icon>
<md-input-container fxFlex>
<input name="jobTitle" formControlName="jobTitle" mdInput placeholder="Job title">
</md-input-container>
</div>
<div class="mb-24" fxLayout="row" fxLayoutAlign="start start">
<md-input-container fxFlex class="mr-24">
<button mdSuffix [mdDatepickerToggle]="birthdayDatePicker"></button>
<input mdInput
name="birthday" formControlName="birthday"
[mdDatepicker]="birthdayDatePicker"
placeholder="Birthday">
</md-input-container>
<md-datepicker #birthdayDatePicker></md-datepicker>
</div>
<div class="mb-24" fxLayout="row" fxLayoutAlign="start start">
<md-icon class="mr-12 mt-12">home</md-icon>
<md-input-container fxFlex>
<input name="address" formControlName="address" mdInput placeholder="Address">
</md-input-container>
</div>
<div fxLayout="row" class="textarea-wrapper" fxLayoutAlign="start start">
<md-icon class="mr-12 mt-12">note</md-icon>
<md-input-container fxFlex>
<textarea name="notes" formControlName="notes" placeholder="Notes" mdInput type="text" max-rows="4"></textarea>
</md-input-container>
</div>
</form>
</div>
<div md-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="space-between center">
<button *ngIf="action !=='edit'"
md-raised-button
(click)="dialogRef.close(contactForm)"
class="save-button mat-accent"
[disabled]="contactForm.invalid"
aria-label="SAVE">
SAVE
</button>
<button *ngIf="action ==='edit'"
md-raised-button
(click)="dialogRef.close(['save',contactForm])"
class="save-button mat-accent"
[disabled]="contactForm.invalid"
aria-label="SAVE">
SAVE
</button>
<button *ngIf="action ==='edit'"
md-button
class="mat-icon-button"
(click)="dialogRef.close(['delete',contactForm])"
aria-label="Delete"
md-tooltip="Delete">
<md-icon>delete</md-icon>
</button>
</div>

View File

@ -1,16 +0,0 @@
.contact-form-dialog {
.mat-dialog-container {
padding: 0;
width: 400px;
.toolbar-bottom {
height: 200px;
}
}
}
:host {
display: flex;
flex-direction: column;
}

View File

@ -1,66 +0,0 @@
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { MD_DIALOG_DATA, MdDialogRef } from '@angular/material';
import { CalendarEvent } from 'angular-calendar';
import { FormBuilder, FormGroup } from '@angular/forms';
import 'rxjs/Rx';
import { Contact } from '../contact.model';
@Component({
selector : 'fuse-contacts-contact-form-dialog',
templateUrl : './contact-form.component.html',
styleUrls : ['./contact-form.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseContactsContactFormDialogComponent implements OnInit
{
event: CalendarEvent;
dialogTitle: string;
contactForm: FormGroup;
action: string;
contact: Contact;
constructor(
public dialogRef: MdDialogRef<FuseContactsContactFormDialogComponent>,
@Inject(MD_DIALOG_DATA) private data: any,
private formBuilder: FormBuilder
)
{
this.action = data.action;
if ( this.action === 'edit' )
{
this.dialogTitle = 'Edit Contact';
this.contact = data.contact;
}
else
{
this.dialogTitle = 'New Contact';
this.contact = new Contact({});
}
this.contactForm = this.createContactForm();
}
ngOnInit()
{
}
createContactForm()
{
return this.formBuilder.group({
id : [this.contact.id],
name : [this.contact.name],
lastName: [this.contact.lastName],
avatar : [this.contact.avatar],
nickname: [this.contact.nickname],
company : [this.contact.company],
jobTitle: [this.contact.jobTitle],
email : [this.contact.email],
phone : [this.contact.phone],
address : [this.contact.address],
birthday: [this.contact.birthday],
notes : [this.contact.notes]
});
}
}

View File

@ -1,109 +0,0 @@
<md-table #table [dataSource]="dataSource">
<!-- Checkbox Column -->
<ng-container cdkColumnDef="checkbox">
<md-header-cell *cdkHeaderCellDef></md-header-cell>
<md-cell *cdkCellDef="let contact">
<md-checkbox [(ngModel)]="checkboxes[contact.id]" (ngModelChange)="onSelectedChange(contact.id)"
(click)="$event.stopPropagation()">
</md-checkbox>
</md-cell>
</ng-container>
<!-- Avatar Column -->
<ng-container cdkColumnDef="avatar">
<md-header-cell *cdkHeaderCellDef></md-header-cell>
<md-cell *cdkCellDef="let contact">
<img class="avatar" *ngIf="contact.avatar" [alt]="contact.name"
[src]="contact.avatar"/>
</md-cell>
</ng-container>
<!-- Name Column -->
<ng-container cdkColumnDef="name">
<md-header-cell *cdkHeaderCellDef>Name</md-header-cell>
<md-cell *cdkCellDef="let contact">
<p class="text-truncate font-weight-600">{{contact.name}} {{contact.lastName}}</p>
</md-cell>
</ng-container>
<!-- Email Column -->
<ng-container cdkColumnDef="email">
<md-header-cell *cdkHeaderCellDef fxHide fxShow.gt-sm>Email</md-header-cell>
<md-cell *cdkCellDef="let contact" fxHide fxShow.gt-sm>
<p class="email text-truncate">
{{contact.email}}
</p>
</md-cell>
</ng-container>
<!-- Phone Column -->
<ng-container cdkColumnDef="phone">
<md-header-cell *cdkHeaderCellDef fxHide fxShow.gt-md>Phone</md-header-cell>
<md-cell *cdkCellDef="let contact" fxHide fxShow.gt-md>
<p class="phone text-truncate">
{{contact.phone}}
</p>
</md-cell>
</ng-container>
<!-- Job Title Column -->
<ng-container cdkColumnDef="jobTitle">
<md-header-cell *cdkHeaderCellDef fxHide fxShow.gt-lg>Job title</md-header-cell>
<md-cell *cdkCellDef="let contact" fxHide fxShow.gt-lg>
<p class="job-title text-truncate">
{{contact.jobTitle}}
</p>
</md-cell>
</ng-container>
<!-- Company Column -->
<ng-container cdkColumnDef="company">
<md-header-cell *cdkHeaderCellDef fxHide fxShow.gt-lg>Company</md-header-cell>
<md-cell *cdkCellDef="let contact" fxHide fxShow.gt-lg>
<p class="company text-truncate">
{{contact.company}}
</p>
</md-cell>
</ng-container>
<!-- Buttons Column -->
<ng-container cdkColumnDef="buttons">
<md-header-cell *cdkHeaderCellDef></md-header-cell>
<md-cell *cdkCellDef="let contact">
<div fxFlex="row" fxLayoutAlign="end center">
<button md-icon-button (click)="$event.stopPropagation();toggleStar(contact.id)" aria-label="Toggle star">
<md-icon *ngIf="user.starred.includes(contact.id)">star</md-icon>
<md-icon *ngIf="!user.starred.includes(contact.id)">star_outline</md-icon>
</button>
<button md-icon-button [mdMenuTriggerFor]="moreMenu" aria-label="More"
(click)="$event.stopPropagation();">
<md-icon>more_vert</md-icon>
</button>
<md-menu #moreMenu="mdMenu">
<button md-menu-item aria-label="remove" (click)="deleteContact(contact)">
<md-icon>delete</md-icon>
<span>Remove</span>
</button>
</md-menu>
</div>
</md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let contact; columns: displayedColumns;"
(click)="editContact(contact)"
[ngClass]="{'md-light-blue-50-bg':checkboxes[contact.id]}"
md-ripple>
</md-row>
</md-table>
<!-- ADD CONTACT BUTTON -->
<button md-fab class="md-accent-bg" id="add-contact-button" (click)="newContact($event)" aria-label="add contact">
<md-icon>person_add</md-icon>
</button>
<!-- / ADD CONTACT BUTTON -->

View File

@ -1,50 +0,0 @@
@import "src/app/core/scss/fuse";
:host {
flex: 1;
.mat-table {
background: transparent;
box-shadow: none;
.mat-column-checkbox {
flex: 0 1 64px;
}
.mat-column-avatar {
flex: 0 1 64px;
}
.mat-column-buttons {
flex: 0 1 80px;
}
.mat-row {
position: relative;
cursor: pointer;
.mat-cell {
min-width: 0;
&.mat-column-detail-button {
flex: 0 1 auto;
padding: 0 24px 0 0;
@include media-breakpoint('gt-md') {
display: none;
}
}
}
}
}
}
#add-contact-button {
position: fixed;
bottom: 12px;
right: 12px;
padding: 0;
@include media-breakpoint-down('xs') {
top: 12px;
}
}

View File

@ -1,183 +0,0 @@
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { ContactsService } from '../contacts.service';
import { DataSource } from '@angular/cdk';
import { Observable } from 'rxjs/Observable';
import { FuseContactsContactFormDialogComponent } from '../contact-form/contact-form.component';
import { MdDialog, MdDialogRef } from '@angular/material';
import { FuseConfirmDialogComponent } from '../../../../../core/components/confirm-dialog/confirm-dialog.component';
import { FormGroup } from '@angular/forms';
@Component({
selector : 'fuse-contacts-contact-list',
templateUrl: './contact-list.component.html',
styleUrls : ['./contact-list.component.scss']
})
export class FuseContactsContactListComponent implements OnInit
{
@ViewChild('dialogContent') dialogContent: TemplateRef<any>;
contacts: any;
user: any;
dataSource: FilesDataSource | null;
displayedColumns = ['checkbox', 'avatar', 'name', 'email', 'phone', 'jobTitle', 'buttons'];
selectedContacts: any[];
checkboxes: {};
dialogRef: any;
confirmDialogRef: MdDialogRef<FuseConfirmDialogComponent>;
constructor(
private contactsService: ContactsService,
public dialog: MdDialog
)
{
this.contactsService.onContactsChanged.subscribe(contacts => {
this.contacts = contacts;
this.checkboxes = {};
contacts.map(contact => {
this.checkboxes[contact.id] = false;
});
});
this.contactsService.onSelectedContactsChanged.subscribe(selectedContacts => {
for ( const id in this.checkboxes )
{
this.checkboxes[id] = selectedContacts.includes(id);
}
this.selectedContacts = selectedContacts;
});
this.contactsService.onUserDataChanged.subscribe(user => {
this.user = user;
});
}
ngOnInit()
{
this.dataSource = new FilesDataSource(this.contactsService);
}
newContact()
{
this.dialogRef = this.dialog.open(FuseContactsContactFormDialogComponent, {
panelClass: 'contact-form-dialog',
data : {
action: 'new'
}
});
this.dialogRef.afterClosed()
.subscribe((response: FormGroup) => {
if ( !response )
{
return;
}
this.contactsService.updateContact(response.getRawValue());
});
}
editContact(contact)
{
this.dialogRef = this.dialog.open(FuseContactsContactFormDialogComponent, {
panelClass: 'contact-form-dialog',
data : {
contact: contact,
action : 'edit'
}
});
this.dialogRef.afterClosed()
.subscribe(response => {
if ( !response )
{
return;
}
const actionType: string = response[0];
const formData: FormGroup = response[1];
switch ( actionType )
{
/**
* Save
*/
case 'save':
this.contactsService.updateContact(formData.getRawValue());
break;
/**
* Delete
*/
case 'delete':
this.deleteContact(contact);
break;
}
});
}
/**
* Delete Contact
*/
deleteContact(contact)
{
this.confirmDialogRef = this.dialog.open(FuseConfirmDialogComponent, {
disableClose: false
});
this.confirmDialogRef.componentInstance.confirmMessage = 'Are you sure you want to delete?';
this.confirmDialogRef.afterClosed().subscribe(result => {
if ( result )
{
this.contactsService.deleteContact(contact);
}
this.confirmDialogRef = null;
});
}
onSelectedChange(contactId)
{
this.contactsService.toggleSelectedContact(contactId);
}
toggleStar(contactId)
{
if ( this.user.starred.includes(contactId) )
{
this.user.starred.splice(this.user.starred.indexOf(contactId), 1);
}
else
{
this.user.starred.push(contactId);
}
this.contactsService.updateUserData(this.user);
}
}
export class FilesDataSource extends DataSource<any>
{
constructor(private contactsService: ContactsService)
{
super();
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<any[]>
{
return this.contactsService.onContactsChanged;
}
disconnect()
{
}
}

View File

@ -1,35 +0,0 @@
import { FuseUtils } from '../../../../core/fuseUtils';
export class Contact
{
id: string;
name: string;
lastName: string;
avatar: string;
nickname: string;
company: string;
jobTitle: string;
email: string;
phone: string;
address: string;
birthday: string;
notes: string;
constructor(contact)
{
{
this.id = contact.id || FuseUtils.generateGUID();
this.name = contact.name || '';
this.lastName = contact.lastName || '';
this.avatar = contact.avatar || 'assets/images/avatars/profile.jpg';
this.nickname = contact.nickname || '';
this.company = contact.company || '';
this.jobTitle = contact.jobTitle || '';
this.email = contact.email || '';
this.phone = contact.phone || '';
this.address = contact.address || '';
this.birthday = contact.birhday || '';
this.notes = contact.notes || '';
}
}
}

View File

@ -1,69 +0,0 @@
<div id="contacts" class="page-layout simple left-sidenav inner-sidenav" fxLayout="column" perfect-scrollbar>
<!-- HEADER -->
<div class="header md-accent-bg p-24" fxLayout="column" fxLayoutAlign="start start"
fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="space-between center">
<!-- APP TITLE -->
<div fxLayout="row" fxLayoutAlign="start center">
<button md-button class="mat-icon-button sidenav-toggle mr-12"
fuseMdSidenavToggler="contacts-main-sidenav"
fxHide.gt-md>
<md-icon>menu</md-icon>
</button>
<div class="logo" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="logo-icon mr-16">account_box</md-icon>
<span class="logo-text h1">Contacts</span>
</div>
</div>
<!-- / APP TITLE -->
<!-- SEARCH -->
<div class="search-input-wrapper mt-16 ml-8 m-sm-0" fxLayout="row" fxLayoutAlign="start center">
<label for="search" class="mr-8">
<md-icon>search</md-icon>
</label>
<md-input-container md-no-float class="m-0" floatPlaceholder="never">
<input mdInput [formControl]="searchInput" id="search" placeholder="Search for anything">
</md-input-container>
</div>
<!-- / SEARCH -->
</div>
<!-- / HEADER -->
<!-- SELECTED BAR -->
<fuse-selected-bar class="md-accent-600-bg" *ngIf="hasSelectedContacts" [@slideInTop]></fuse-selected-bar>
<!-- / SELECTED BAR -->
<md-sidenav-container>
<!-- SIDENAV -->
<md-sidenav class="sidenav mat-sidenav-opened" align="start" opened="true" mode="side"
fuseMdSidenavHelper="contacts-main-sidenav" md-is-locked-open="gt-md">
<fuse-contacts-main-sidenav></fuse-contacts-main-sidenav>
</md-sidenav>
<!-- / SIDENAV -->
<!-- CENTER -->
<div class="center p-24 pr-sm-92" perfect-scrollbar>
<!-- CONTENT -->
<div class="content md-white-bg mat-elevation-z4">
<fuse-contacts-contact-list></fuse-contacts-contact-list>
</div>
<!-- / CONTENT -->
</div>
<!-- / CENTER -->
</md-sidenav-container>
</div>

View File

@ -1,8 +0,0 @@
@import "src/app/core/scss/fuse";
#contacts {
.content {
overflow: hidden;
}
}

View File

@ -1,39 +0,0 @@
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ContactsService } from './contacts.service';
import { Animations } from '../../../../core/animations';
import { FormControl } from '@angular/forms';
@Component({
selector : 'fuse-contacts',
templateUrl : './contacts.component.html',
styleUrls : ['./contacts.component.scss'],
encapsulation: ViewEncapsulation.None,
animations : [Animations.slideInTop]
})
export class FuseContactsComponent implements OnInit
{
hasSelectedContacts: boolean;
searchInput: FormControl;
constructor(private contactsService: ContactsService)
{
this.searchInput = new FormControl('');
}
ngOnInit()
{
this.contactsService.onSelectedContactsChanged
.subscribe(selectedContacts => {
this.hasSelectedContacts = selectedContacts.length > 0;
});
this.searchInput.valueChanges
.debounceTime(300)
.distinctUntilChanged()
.subscribe(searchText => {
this.contactsService.onSearchTextChanged.next(searchText);
});
}
}

View File

@ -1,41 +0,0 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../../../../core/modules/shared.module';
import { RouterModule, Routes } from '@angular/router';
import { FuseContactsMainSidenavComponent } from './sidenavs/main/main.component';
import { FuseContactsComponent } from './contacts.component';
import { ContactsService } from './contacts.service';
import { FuseContactsContactListComponent } from './contact-list/contact-list.component';
import { FuseContactsSelectedBarComponent } from './selected-bar/selected-bar.component';
import { FuseContactsContactFormDialogComponent } from './contact-form/contact-form.component';
const routes: Routes = [
{
path : '**',
component: FuseContactsComponent,
children : [],
resolve : {
contacts: ContactsService
}
}
];
@NgModule({
imports : [
SharedModule,
RouterModule.forChild(routes)
],
declarations : [
FuseContactsComponent,
FuseContactsContactListComponent,
FuseContactsSelectedBarComponent,
FuseContactsMainSidenavComponent,
FuseContactsContactFormDialogComponent
],
providers : [
ContactsService
],
entryComponents: [FuseContactsContactFormDialogComponent]
})
export class FuseContactsModule
{
}

View File

@ -1,243 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Http } from '@angular/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Contact } from './contact.model';
import { FuseUtils } from '../../../../core/fuseUtils';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class ContactsService implements Resolve<any>
{
onContactsChanged: BehaviorSubject<any> = new BehaviorSubject({});
onSelectedContactsChanged: BehaviorSubject<any> = new BehaviorSubject([]);
onUserDataChanged: BehaviorSubject<any> = new BehaviorSubject([]);
onSearchTextChanged: Subject<any> = new Subject();
onFilterChanged: Subject<any> = new Subject();
contacts: Contact[];
user: any;
selectedContacts: string[] = [];
searchText: string;
filterBy: string;
constructor(private http: Http)
{
}
/**
* The Contacts App Main Resolver
* @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any}
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
return new Promise((resolve, reject) => {
Promise.all([
this.getContacts(),
this.getUserData()
]).then(
([files]) => {
this.onSearchTextChanged.subscribe(searchText => {
this.searchText = searchText;
this.getContacts();
});
this.onFilterChanged.subscribe(filter => {
this.filterBy = filter;
this.getContacts();
});
resolve();
},
reject
);
});
}
getContacts(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/contacts-contacts')
.subscribe(response => {
this.contacts = response.json().data;
if ( this.filterBy === 'starred' )
{
this.contacts = this.contacts.filter(_contact => {
return this.user.starred.includes(_contact.id);
});
}
if ( this.filterBy === 'frequent' )
{
this.contacts = this.contacts.filter(_contact => {
return this.user.frequentContacts.includes(_contact.id);
});
}
if ( this.searchText && this.searchText !== '' )
{
this.contacts = FuseUtils.filterArrayByString(this.contacts, this.searchText);
}
this.contacts = this.contacts.map(contact => {
return new Contact(contact);
});
this.onContactsChanged.next(this.contacts);
resolve(this.contacts);
}, reject);
}
);
}
getUserData(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/contacts-user/5725a6802d10e277a0f35724')
.subscribe(response => {
this.user = response.json().data;
this.onUserDataChanged.next(this.user);
resolve(this.user);
}, reject);
}
);
}
/**
* Toggle selected contact by id
* @param id
*/
toggleSelectedContact(id)
{
// First, check if we already have that todo as selected...
if ( this.selectedContacts.length > 0 )
{
const index = this.selectedContacts.indexOf(id);
if ( index !== -1 )
{
this.selectedContacts.splice(index, 1);
// Trigger the next event
this.onSelectedContactsChanged.next(this.selectedContacts);
// Return
return;
}
}
// If we don't have it, push as selected
this.selectedContacts.push(id);
// Trigger the next event
this.onSelectedContactsChanged.next(this.selectedContacts);
}
/**
* Toggle select all
*/
toggleSelectAll()
{
if ( this.selectedContacts.length > 0 )
{
this.deselectContacts();
}
else
{
this.selectContacts();
}
}
selectContacts(filterParameter?, filterValue?)
{
this.selectedContacts = [];
// If there is no filter, select all todos
if ( filterParameter === undefined || filterValue === undefined )
{
this.selectedContacts = [];
this.contacts.map(contact => {
this.selectedContacts.push(contact.id);
});
}
else
{
/* this.selectedContacts.push(...
this.contacts.filter(todo => {
return todo[filterParameter] === filterValue;
})
);*/
}
// Trigger the next event
this.onSelectedContactsChanged.next(this.selectedContacts);
}
updateContact(contact)
{
return new Promise((resolve, reject) => {
this.http.post('api/contacts-contacts/' + contact.id, {...contact})
.subscribe(response => {
this.getContacts();
resolve(response);
});
});
}
updateUserData(userData)
{
return new Promise((resolve, reject) => {
this.http.post('api/contacts-user/' + this.user.id, {...userData})
.subscribe(response => {
this.getUserData();
this.getContacts();
resolve(response);
});
});
}
deselectContacts()
{
this.selectedContacts = [];
// Trigger the next event
this.onSelectedContactsChanged.next(this.selectedContacts);
}
deleteContact(contact)
{
const contactIndex = this.contacts.indexOf(contact);
this.contacts.splice(contactIndex, 1);
this.onContactsChanged.next(this.contacts);
}
deleteSelectedContacts()
{
for ( const contactId of this.selectedContacts )
{
const contact = this.contacts.find(_contact => {
return _contact.id === contactId;
});
const contactIndex = this.contacts.indexOf(contact);
this.contacts.splice(contactIndex, 1);
}
this.onContactsChanged.next(this.contacts);
this.deselectContacts();
}
}

View File

@ -1,37 +0,0 @@
<div fxFlex fxLayout="row" fxLayoutAlign="start center" class="p-24">
<div class="close-button-wrapper" fxFlex="220px" (click)="deselectAll()">
<button class="p-8" md-button fxLayout="row" fxLayoutAlign="start center">
<md-icon class="mr-8">arrow_back</md-icon>
<span class="text-uppercase">Back</span>
</button>
</div>
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<div>
<span class="selected-contacts-count">
<span>{{selectedContacts.length}}</span>
<span>selected</span>
</span>
<button md-icon-button [mdMenuTriggerFor]="selectMenu">
<md-icon>arrow_drop_down</md-icon>
</button>
<md-menu #selectMenu="mdMenu">
<button md-menu-item (click)="selectAll()">Select all</button>
<button md-menu-item (click)="deselectAll()">Deselect all</button>
</md-menu>
</div>
<div class="multi-select-actions">
<button md-icon-button (click)="deleteSelectedContacts($event)"
aria-label="delete selected">
<md-icon>delete</md-icon>
</button>
</div>
</div>
</div>

View File

@ -1,11 +0,0 @@
@import "src/app/core/scss/fuse";
:host {
flex: 1;
position: absolute;
top: 0;
right: 0;
left: 0;
height: 120px;
z-index: 99;
}

View File

@ -1,65 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ContactsService } from '../contacts.service';
import { MdDialog, MdDialogRef } from '@angular/material';
import { FuseConfirmDialogComponent } from '../../../../../core/components/confirm-dialog/confirm-dialog.component';
@Component({
selector : 'fuse-selected-bar',
templateUrl: './selected-bar.component.html',
styleUrls : ['./selected-bar.component.scss']
})
export class FuseContactsSelectedBarComponent implements OnInit
{
selectedContacts: string[];
hasSelectedContacts: boolean;
isIndeterminate: boolean;
confirmDialogRef: MdDialogRef<FuseConfirmDialogComponent>;
constructor(
private contactsService: ContactsService,
public dialog: MdDialog
)
{
this.contactsService.onSelectedContactsChanged
.subscribe(selectedContacts => {
this.selectedContacts = selectedContacts;
setTimeout(() => {
this.hasSelectedContacts = selectedContacts.length > 0;
this.isIndeterminate = (selectedContacts.length !== this.contactsService.contacts.length && selectedContacts.length > 0);
}, 0);
});
}
ngOnInit()
{
}
selectAll()
{
this.contactsService.selectContacts();
}
deselectAll()
{
this.contactsService.deselectContacts();
}
deleteSelectedContacts()
{
this.confirmDialogRef = this.dialog.open(FuseConfirmDialogComponent, {
disableClose: false
});
this.confirmDialogRef.componentInstance.confirmMessage = 'Are you sure you want to delete all selected contacts?';
this.confirmDialogRef.afterClosed().subscribe(result => {
if ( result )
{
this.contactsService.deleteSelectedContacts();
}
this.confirmDialogRef = null;
});
}
}

View File

@ -1,42 +0,0 @@
<div class="sidenav-content">
<div class="card md-white-bg">
<!-- SIDENAV HEADER -->
<div class="header p-24" fxLayout="row" fxLayoutAlign="start center">
<!-- USER -->
<img [src]="user.avatar" class="avatar mr-16" [alt]="user.name"/>
<span class="h5">{{user.name}}</span>
<!-- / USER -->
</div>
<!-- / SIDENAV HEADER -->
<!-- SIDENAV CONTENT -->
<div class="content py-16" perfect-scrollbar>
<div class="nav">
<div class="nav-item" aria-label="inbox">
<a class="nav-link" md-ripple (click)="changeFilter('all')" [ngClass]="{'active':filterBy ==='all'}">
<span class="title">All Contacts</span>
</a>
</div>
<div class="nav-item" aria-label="frequently contacted" (click)="changeFilter('frequent')">
<a class="nav-link" md-ripple [ngClass]="{'active':filterBy ==='frequent'}">
<div class="title">Freequently contacted</div>
</a>
</div>
<div class="nav-item" aria-label="starred" (click)="changeFilter('starred')">
<a class="nav-link" md-ripple [ngClass]="{'active':filterBy ==='starred'}">
<div class="title">Starred Contacts</div>
</a>
</div>
</div>
</div>
</div>
</div>
<!-- / SIDENAV CONTENT -->

View File

@ -1,39 +0,0 @@
@import "src/app/core/scss/fuse";
:host {
display: flex;
flex-direction: column;
flex: 1 0 auto;
height: 100%;
.sidenav-content {
display: flex;
flex-direction: column;
padding: 0;
@include media-breakpoint(gt-md) {
padding: 24px 4px 24px 24px;
}
.card {
display: flex;
flex-direction: column;
flex: 0 1 auto;
padding: 0;
@include media-breakpoint(gt-md) {
@include mat-elevation(4);
}
> .header {
flex: 0 1 auto;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
> .content {
flex: 0 1 auto;
}
}
}
}

View File

@ -1,31 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ContactsService } from '../../contacts.service';
@Component({
selector : 'fuse-contacts-main-sidenav',
templateUrl: './main.component.html',
styleUrls : ['./main.component.scss']
})
export class FuseContactsMainSidenavComponent implements OnInit
{
user: any;
filterBy: string;
constructor(private contactsService: ContactsService)
{
this.filterBy = 'all';
this.contactsService.onUserDataChanged.subscribe(user => {
this.user = user;
});
}
ngOnInit()
{
}
changeFilter(filter)
{
this.filterBy = filter;
this.contactsService.onFilterChanged.next(this.filterBy);
}
}

View File

@ -1,831 +0,0 @@
<div id="dashboard-project" class="page-layout simple right-sidenav" fxLayout="row" perfect-scrollbar>
<md-sidenav-container>
<!-- CENTER -->
<div class="center" fxFlex perfect-scrollbar>
<!-- HEADER -->
<div class="header md-accent-bg p-24 pb-0" fxLayout="column" fxLayoutAlign="space-between">
<div fxLayout="row" fxLayoutAlign="space-between start">
<span class="mat-display-1 mb-0">Welcome back, John!</span>
<button md-icon-button fuseMdSidenavToggler="dashboards-right-sidenav" fxHide.gt-md>
<md-icon>menu</md-icon>
</button>
</div>
<div fxLayout="row">
<div class="selected-project">{{selectedProject.name}}</div>
<button md-icon-button class="project-selector" [mdMenuTriggerFor]="projectsMenu" aria-label="Select project">
<md-icon>more_horiz</md-icon>
</button>
<md-menu #projectsMenu="mdMenu">
<button md-menu-item *ngFor="let project of projects" (click)="selectedProject = project">
<span>{{project.name}}</span>
</button>
</md-menu>
</div>
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content">
<md-tab-group dynamicHeight>
<md-tab label="Home">
<div class="widget-group p-12" fxLayout="row" fxFlex="100" fxLayoutWrap>
<!-- WIDGET 1 -->
<fuse-widget class="" fxLayout="column" fxFlex="100" fxFlex.gt-xs="50" fxFlex.gt-md="25">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row" fxLayoutAlign="space-between center">
<md-select class="simplified font-size-16" [(ngModel)]="widgets.widget1.currentRange"
aria-label="Change range">
<md-option *ngFor="let range of widgets.widget1.ranges | keys"
[value]="range.key">
{{range.value}}
</md-option>
</md-select>
<button md-icon-button fuseWidgetToggle aria-label="more">
<md-icon>more_vert</md-icon>
</button>
</div>
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
<div class="light-blue-fg font-size-72 line-height-72">
{{widgets.widget1.data.count[widgets.widget1.currentRange]}}
</div>
<div class="h3 secondary-text font-weight-500">{{widgets.widget1.data.label}}</div>
</div>
<div class="p-16 grey-50-bg border-top" fxLayout="row" fxLayoutAlign="start center">
<span class="h4 secondary-text text-truncate">{{widgets.widget1.data.extra.label}}:</span>
<span class="h4 ml-8">{{widgets.widget1.data.extra.count[widgets.widget1.currentRange]}}</span>
</div>
</div>
<!-- / Front -->
<!-- Back -->
<div class="fuse-widget-back p-16 pt-32 md-white-bg mat-elevation-z2">
<button md-icon-button fuseWidgetToggle class="fuse-widget-flip-button" ng-click="flipWidget()" aria-label="Flip widget">
<md-icon class="s-16">close</md-icon>
</button>
<div>
{{widgets.widget1.detail}}
</div>
</div>
<!-- / Back -->
</fuse-widget>
<!-- / WIDGET 1 -->
<!-- WIDGET 2 -->
<fuse-widget class="" fxLayout="column" fxFlex="100" fxFlex.gt-xs="50" fxFlex.gt-md="25">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h3">{{widgets.widget2.title}}</div>
<button md-icon-button fuseWidgetToggle class="fuse-widget-flip-button" aria-label="more">
<md-icon>more_vert</md-icon>
</button>
</div>
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
<div class="red-fg font-size-72 line-height-72">
{{widgets.widget2.data.count}}
</div>
<div class="h3 secondary-text font-weight-500">{{widgets.widget2.data.label}}</div>
</div>
<div class="p-16 grey-50-bg border-top" fxLayout="row" fxLayoutAlign="start center">
<span class="h4 secondary-text text-truncate">{{widgets.widget2.data.extra.label}}:</span>
<span class="h4 ml-8">{{widgets.widget2.data.extra.count}}</span>
</div>
</div>
<!-- / Front -->
<!-- Back -->
<div class="fuse-widget-back p-16 pt-32 md-white-bg mat-elevation-z2">
<button md-icon-button fuseWidgetToggle class="fuse-widget-flip-button" ng-click="flipWidget()" aria-label="Flip widget">
<md-icon class="s-16">close</md-icon>
</button>
<div>
{{widgets.widget2.detail}}
</div>
</div>
<!-- / Back -->
</fuse-widget>
<!-- / WIDGET 2 -->
<!-- WIDGET 3 -->
<fuse-widget class="" fxLayout="column" fxFlex="100" fxFlex.gt-xs="50" fxFlex.gt-md="25">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h3">{{widgets.widget3.title}}</div>
<button md-icon-button fuseWidgetToggle class="fuse-widget-flip-button" aria-label="more">
<md-icon>more_vert</md-icon>
</button>
</div>
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
<div class="orange-fg font-size-72 line-height-72">
{{widgets.widget3.data.count}}
</div>
<div class="h3 secondary-text font-weight-500">{{widgets.widget3.data.label}}</div>
</div>
<div class="p-16 grey-50-bg border-top" fxLayout="row" fxLayoutAlign="start center">
<span class="h4 secondary-text text-truncate">{{widgets.widget3.data.extra.label}}:</span>
<span class="h4 ml-8">{{widgets.widget3.data.extra.count}}</span>
</div>
</div>
<!-- / Front -->
<!-- Back -->
<div class="fuse-widget-back p-16 pt-32 md-white-bg mat-elevation-z2">
<button md-icon-button fuseWidgetToggle class="fuse-widget-flip-button" ng-click="flipWidget()" aria-label="Flip widget">
<md-icon class="s-16">close</md-icon>
</button>
<div>
{{widgets.widget3.detail}}
</div>
</div>
<!-- / Back -->
</fuse-widget>
<!-- / WIDGET 3 -->
<!-- WIDGET 4 -->
<fuse-widget class="" fxLayout="column" fxFlex="100" fxFlex.gt-xs="50" fxFlex.gt-md="25">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h3">{{widgets.widget4.title}}</div>
<button md-icon-button fuseWidgetToggle class="fuse-widget-flip-button" aria-label="more">
<md-icon>more_vert</md-icon>
</button>
</div>
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
<div class="blue-grey-fg font-size-72 line-height-72">{{widgets.widget4.data.count}}
</div>
<div class="h3 secondary-text font-weight-500">{{widgets.widget4.data.label}}</div>
</div>
<div class="p-16 grey-50-bg border-top" fxLayout="row" fxLayoutAlign="start center">
<span class="h4 secondary-text text-truncate">{{widgets.widget4.data.extra.label}}:</span>
<span class="h4 ml-8">{{widgets.widget4.data.extra.count}}</span>
</div>
</div>
<!-- / Front -->
<!-- Back -->
<div class="fuse-widget-back p-16 pt-32 md-white-bg mat-elevation-z2">
<button md-icon-button fuseWidgetToggle class="fuse-widget-flip-button" ng-click="flipWidget()" aria-label="Flip widget">
<md-icon class="s-16">close</md-icon>
</button>
<div>
{{widgets.widget4.detail}}
</div>
</div>
<!-- / Back -->
</fuse-widget>
<!-- / WIDGET 4 -->
<!-- WIDGET 5 -->
<fuse-widget class="" fxLayout="row" fxFlex="100">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="px-16 border-bottom" fxLayout="row" fxLayoutAlign="space-between center" fxLayoutWrap>
<div fxFlex class="py-8 h3">{{widgets.widget5.title}}</div>
<div fxFlex="0 1 auto" class="py-8" fxLayout="row">
<button md-button class="px-16"
*ngFor="let range of widgets.widget5.ranges | keys"
(click)="widget5.currentRange = range.key"
[disabled]="widget5.currentRange == range.key">
{{range.value}}
</button>
</div>
</div>
<div fxLayout="row" fxLayoutAlign="start end" fxLayoutWrap>
<div class="h-420 mb-16" fxLayout="row" fxFlex="100" fxFlex.gt-sm="50">
<ngx-charts-bar-vertical-stacked
*fuseIfOnDom
[scheme]="widget5.scheme"
[results]="this.widgets.widget5.mainChart[this.widget5.currentRange]"
[gradient]="widget5.gradient"
[xAxis]="widget5.xAxis"
[yAxis]="widget5.yAxis"
[legend]="widget5.legend"
[showXAxisLabel]="widget5.showXAxisLabel"
[showYAxisLabel]="widget5.showYAxisLabel"
[xAxisLabel]="widget5.xAxisLabel"
[yAxisLabel]="widget5.yAxisLabel"
(select)="widget5.onSelect($event)">
</ngx-charts-bar-vertical-stacked>
</div>
<div class="mb-16" fxFlex="100" fxFlex.gt-sm="50" fxLayoutWrap>
<div fxLayout="column" fxFlex="100" fxFlex.gt-sm="50" fxLayoutAlign="center"
*ngFor="let widget of widgets.widget5.supporting | keys">
<div class="px-24">
<div class="h4 secondary-text">{{widget.value.label}}</div>
<div class="mat-display-1 m-0">
{{widget.value.count[widget5.currentRange]}}
</div>
</div>
<div class="h-64">
<ngx-charts-area-chart
*fuseIfOnDom
[results]="widget.value.chart[widget5.currentRange]"
[scheme]="widget5.supporting.scheme"
[gradient]="widget5.supporting.gradient"
[xAxis]="widget5.supporting.xAxis"
[yAxis]="widget5.supporting.yAxis"
[legend]="widget5.supporting.legend"
[showXAxisLabel]="widget5.supporting.showXAxisLabel"
[showYAxisLabel]="widget5.supporting.showYAxisLabel"
[xAxisLabel]="widget5.supporting.xAxisLabel"
[yAxisLabel]="widget5.supporting.yAxisLabel"
[curve]="widget5.supporting.curve">
>
</ngx-charts-area-chart>
</div>
</div>
</div>
</div>
</div>
<!-- / Front -->
</fuse-widget>
<!-- / WIDGET 5 -->
<!-- WIDGET 6 -->
<fuse-widget class="" fxLayout="column" fxFlex="100" fxFlex.gt-sm="50">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="px-16 border-bottom" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h3">{{widgets.widget6.title}}</div>
<div class="py-16">
<md-select class="simplified" [(ngModel)]="widget6.currentRange" aria-label="Change range">
<md-option *ngFor="let range of widgets.widget6.ranges | keys"
[value]="range.key">
{{range.value}}
</md-option>
</md-select>
</div>
</div>
<div class="h-400">
<ngx-charts-pie-chart
*fuseIfOnDom
[scheme]="widget6.scheme"
[results]="widgets.widget6.mainChart[widget6.currentRange]"
[legend]="widget6.showLegend"
[explodeSlices]="widget6.explodeSlices"
[labels]="widget6.labels"
[doughnut]="widget6.doughnut"
[gradient]="widget6.gradient"
(select)="widget6.onSelect($event)">
</ngx-charts-pie-chart>
</div>
<div class="py-8 mh-16 border-top" fxLayout="row" fxLayoutAlign="start center" fxLayoutWrap>
<div class="py-8 border-right" fxLayout="column" fxLayoutAlign="center center" fxFlex="100" fxFlex.gt-sm="50">
<span class="mat-display-1 mb-0">{{widgets.widget6.footerLeft.count[widget6.currentRange]}}</span>
<span class="h4">{{widgets.widget6.footerLeft.title}}</span>
</div>
<div class="py-8" fxLayout="column" fxLayoutAlign="center center" fxFlex="100" fxFlex.gt-sm="50">
<span class="mat-display-1 mb-0">{{widgets.widget6.footerRight.count[widget6.currentRange]}}</span>
<span class="h4">{{widgets.widget6.footerRight.title}}</span>
</div>
</div>
</div>
<!-- / Front -->
</fuse-widget>
<!-- / WIDGET 6 -->
<!-- WIDGET 7 -->
<fuse-widget class="" fxLayout="column" fxFlex="100" fxFlex.gt-sm="50">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="px-16 border-bottom" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h3">{{widgets.widget7.title}}</div>
<div class="py-16">
<md-select class="simplified" [(ngModel)]="widget7.currentRange"
aria-label="Change range">
<md-option *ngFor="let range of widgets.widget7.ranges | keys"
[value]="range.key">
{{range.value}}
</md-option>
</md-select>
</div>
</div>
<div class="p-16" fxLayout="row" fxLayoutAlign="space-between center"
*ngFor="let event of widgets.widget7.schedule[widget7.currentRange]">
<div>
<div class="h3">{{event.title}}</div>
<div>
<span class="secondary-text">{{event.time}}</span>
<span *ngIf="event.location">, {{event.location}}</span>
</div>
</div>
<button md-icon-button aria-label="More information">
<md-icon>more_vert</md-icon>
</button>
</div>
</div>
<!-- / Front -->
</fuse-widget>
<!-- / WIDGET 7 -->
</div>
<!-- / WIDGET GROUP -->
</md-tab>
<md-tab label="Budget Summary">
<!-- WIDGET GROUP -->
<div class="widget-group" fxLayout="row" fxFlex="100" fxLayoutWrap>
<!-- WIDGET 8 -->
<fuse-widget class="" fxLayout="column" fxFlex="100" fxFlex.gt-sm="50">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="h3 p-16">
{{widgets.widget8.title}}
</div>
<md-divider></md-divider>
<div class="h-400">
<ngx-charts-pie-chart
*fuseIfOnDom
[scheme]="widget8.scheme"
[results]="widgets.widget8.mainChart"
[legend]="widget8.legend"
[explodeSlices]="widget8.explodeSlices"
[labels]="widget8.labels"
[doughnut]="widget8.doughnut"
[gradient]="widget8.gradient"
(select)="widget8.onSelect($event)">
</ngx-charts-pie-chart>
</div>
</div>
<!-- / Front -->
</fuse-widget>
<!-- / WIDGET 8 -->
<!-- WIDGET 9 -->
<fuse-widget class="" fxLayout="column" fxFlex="100" fxFlex.gt-sm="50">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="px-16 border-bottom" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h3">{{widgets.widget9.title}}</div>
<div class="py-16">
<md-select [(ngModel)]="widget9.currentRange" aria-label="Change range">
<md-option *ngFor="let range of widgets.widget9.ranges | keys"
[value]="range.key">
{{range.value}}
</md-option>
</md-select>
</div>
</div>
<div class="p-16" fxLayout="column" fxLayoutAlign="center" fxLayout.gt-xs="row"
fxLayoutAlign.gt-xs="space-between end">
<div fxFlex="0 1 auto">
<div class="h4 secondary-text">{{widgets.widget9.weeklySpent.title}}</div>
<div class="pt-8 mat-display-1 m-0 font-weight-300 text-nowrap">
<span class="secondary-text">$</span>
<span>{{widgets.widget9.weeklySpent.count[widget9.currentRange]}}</span>
</div>
</div>
<div class="h-52" fxFlex>
<ngx-charts-area-chart
*fuseIfOnDom
[results]="widgets.widget9.weeklySpent.chart[widget9.currentRange]"
[scheme]="widget9.scheme"
[gradient]="widget9.gradient"
[xAxis]="widget9.xAxis"
[yAxis]="widget9.yAxis"
[legend]="widget9.legend"
[showXAxisLabel]="widget9.showXAxisLabel"
[showYAxisLabel]="widget9.showYAxisLabel"
[xAxisLabel]="widget9.xAxisLabel"
[yAxisLabel]="widget9.yAxisLabel"
[curve]="widget9.curve">
</ngx-charts-area-chart>
</div>
</div>
<div class="p-16" fxLayout="column" fxLayoutAlign="center" fxLayout.gt-xs="row"
fxLayoutAlign.gt-xs="space-between end">
<div fxFlex="0 1 auto">
<div class="h4 secondary-text">{{widgets.widget9.totalSpent.title}}</div>
<div class="pt-8 mat-display-1 m-0 font-weight-300 text-nowrap">
<span class="secondary-text">$</span>
<span>{{widgets.widget9.totalSpent.count[widget9.currentRange]}}</span>
</div>
</div>
<div class="h-52" fxFlex>
<ngx-charts-area-chart
*fuseIfOnDom
[results]="widgets.widget9.totalSpent.chart[widget9.currentRange]"
[scheme]="widget9.scheme"
[gradient]="widget9.gradient"
[xAxis]="widget9.xAxis"
[yAxis]="widget9.yAxis"
[legend]="widget9.legend"
[showXAxisLabel]="widget9.showXAxisLabel"
[showYAxisLabel]="widget9.showYAxisLabel"
[xAxisLabel]="widget9.xAxisLabel"
[yAxisLabel]="widget9.yAxisLabel"
[curve]="widget9.curve">
</ngx-charts-area-chart>
</div>
</div>
<div class="p-16" fxLayout="column" fxLayoutAlign="center" fxLayout.gt-xs="row"
fxLayoutAlign.gt-xs="space-between end">
<div fxFlex="0 1 auto">
<div class="h4 secondary-text">{{widgets.widget9.remaining.title}}</div>
<div class="pt-8 mat-display-1 m-0 font-weight-300 text-nowrap">
<span class="secondary-text">$</span>
<span>{{widgets.widget9.remaining.count[widget9.currentRange]}}</span>
</div>
</div>
<div class="h-52" fxFlex>
<ngx-charts-area-chart
*fuseIfOnDom
[results]="widgets.widget9.remaining.chart[widget9.currentRange]"
[scheme]="widget9.scheme"
[gradient]="widget9.gradient"
[xAxis]="widget9.xAxis"
[yAxis]="widget9.yAxis"
[legend]="widget9.legend"
[showXAxisLabel]="widget9.showXAxisLabel"
[showYAxisLabel]="widget9.showYAxisLabel"
[xAxisLabel]="widget9.xAxisLabel"
[yAxisLabel]="widget9.yAxisLabel"
[curve]="widget9.curve">
</ngx-charts-area-chart>
</div>
</div>
<div class="p-16 border-top">
<div class="h4 secondary-text">{{widgets.widget9.totalBudget.title}}</div>
<div class="pt-8 mat-display-1 m-0 font-weight-300">
<span class="secondary-text">$</span>
<span>{{widgets.widget9.totalBudget.count}}</span>
</div>
</div>
</div>
<!-- / Front -->
</fuse-widget>
<!-- / WIDGET 9 -->
<!-- WIDGET 10 -->
<fuse-widget class="" fxLayout="row" fxFlex="100">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="simple-table-container" ms-responsive-table>
<div class=" table-title">
{{widgets.widget10.title}}
</div>
<table class="simple">
<thead>
<tr>
<th *ngFor="let column of widgets.widget10.table.columns">
{{column.title}}
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of widgets.widget10.table.rows">
<td *ngFor="let cell of row">
<span class="p-4" [class]="cell.classes">
{{cell.value}}
</span>
<md-icon *ngIf="cell.icon" class="s-16">{{cell.icon}}</md-icon>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- / Front -->
</fuse-widget>
<!-- / WIDGET 10 -->
</div>
<!-- / WIDGET GROUP -->
</md-tab>
<md-tab label="Team Members">
<!-- WIDGET GROUP -->
<div class="widget-group" fxLayout="row" fxFlex="100" fxLayoutWrap>
<!-- WIDGET 11 -->
<fuse-widget class="" fxLayout="row" fxFlex="100">
<!-- Front -->
<div class="fuse-widget-front md-white-bg mat-elevation-z2">
<div class="p-24 mb-8 border-bottom" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h2">{{widgets.widget11.title}}</div>
<div class="text-boxed red-bg white-fg m-0">{{widgets.widget11.table.rows.length}}
members
</div>
</div>
<md-table #table [dataSource]="widget11.dataSource">
<!-- Avatar Column -->
<ng-container cdkColumnDef="avatar">
<md-header-cell fxFlex="64px" *cdkHeaderCellDef></md-header-cell>
<md-cell fxFlex="64px" *cdkCellDef="let contact">
<img class="avatar" *ngIf="contact.avatar" [alt]="contact.name"
[src]="contact.avatar"/>
</md-cell>
</ng-container>
<!-- Name Column -->
<ng-container cdkColumnDef="name">
<md-header-cell *cdkHeaderCellDef>Name</md-header-cell>
<md-cell *cdkCellDef="let contact">
<p class="text-truncate font-weight-600">{{contact.name}} {{contact.lastName}}</p>
</md-cell>
</ng-container>
<!-- Position Column -->
<ng-container cdkColumnDef="position">
<md-header-cell *cdkHeaderCellDef fxHide fxShow.gt-sm>Position</md-header-cell>
<md-cell *cdkCellDef="let contact" fxHide fxShow.gt-sm>
<p class="position text-truncate">
{{contact.position}}
</p>
</md-cell>
</ng-container>
<!-- Office Column -->
<ng-container cdkColumnDef="office">
<md-header-cell *cdkHeaderCellDef fxHide fxShow.gt-md>Office</md-header-cell>
<md-cell *cdkCellDef="let contact" fxHide fxShow.gt-md>
<p class="office text-truncate">
{{contact.office}}
</p>
</md-cell>
</ng-container>
<!-- Email Column -->
<ng-container cdkColumnDef="email">
<md-header-cell *cdkHeaderCellDef fxHide fxShow.gt-sm>Email</md-header-cell>
<md-cell *cdkCellDef="let contact" fxHide fxShow.gt-sm>
<p class="email text-truncate">
{{contact.email}}
</p>
</md-cell>
</ng-container>
<!-- Phone Column -->
<ng-container cdkColumnDef="phone">
<md-header-cell *cdkHeaderCellDef fxHide fxShow.gt-md>Phone</md-header-cell>
<md-cell *cdkCellDef="let contact" fxHide fxShow.gt-md>
<p class="phone text-truncate">
{{contact.phone}}
</p>
</md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="widgets.widget11.table.columns"></md-header-row>
<md-row *cdkRowDef="let contact; columns: widgets.widget11.table.columns;">
</md-row>
</md-table>
</div>
<!-- / Front -->
</fuse-widget>
<!-- / WIDGET 11 -->
</div>
<!-- / WIDGET GROUP -->
</md-tab>
</md-tab-group>
</div>
<!-- / CONTENT -->
</div>
<!-- / CENTER -->
<!-- SIDENAV -->
<md-sidenav class="sidenav mat-sidenav-opened" align="end" opened="true" mode="side" fuseMdSidenavHelper="dashboards-right-sidenav" md-is-locked-open="gt-md">
<div class="sidenav-content" perfect-scrollbar>
<!-- WIDGET GROUP -->
<div class="widget-group" fxLayout="column" fxFlex="100">
<!-- NOW WIDGET -->
<fuse-widget class="sidenav-widget p-0">
<!-- Front -->
<div class="fuse-widget-front">
<div class="pl-16 pr-8 py-16" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h3">{{dateNow | date: 'EEEE, h:m:ss'}}</div>
<div>
<button md-icon-button [mdMenuTriggerFor]="moreMenu" aria-label="more">
<md-icon>more_vert</md-icon>
</button>
<md-menu #moreMenu="mdMenu">
<button md-menu-item aria-label="Flip widget">
Details
</button>
</md-menu>
</div>
</div>
<div class="p-16 pb-24" fxLayout="column" fxLayoutAlign="center center">
<div class="h1 secondary-text">
<span>{{dateNow | date: 'MMMM'}}</span>
</div>
<div class="font-size-72 line-height-88 secondary-text font-weight-400">
{{dateNow | date: 'd'}}
</div>
<div class="h1 secondary-text">
<span>{{dateNow | date: 'y'}}</span>
</div>
</div>
<md-divider></md-divider>
</div>
<!-- / Front -->
</fuse-widget>
<!-- / NOW WIDGET -->
<!-- WEATHER WIDGET -->
<fuse-widget class="sidenav-widget p-0">
<!-- Front -->
<div class="fuse-widget-front">
<div class="pl-16 pr-8 py-16" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h4" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="mr-8">place</md-icon>
{{widgets.weatherWidget.locations[widgets.weatherWidget.currentLocation].name}}
</div>
<div>
<button md-icon-button [mdMenuTriggerFor]="moreMenu" aria-label="more">
<md-icon>more_vert</md-icon>
</button>
<md-menu #moreMenu="mdMenu">
<button md-menu-item aria-label="Flip widget">
Details
</button>
</md-menu>
</div>
</div>
<div class="p-16 pb-32" fxLayout="column" fxLayoutAlign="center center">
<div fxLayout="row" fxLayoutAlign="center center">
<md-icon fontSet="meteocons" [fontIcon]="widgets.weatherWidget.locations[widgets.weatherWidget.currentLocation].icon"
class="icon s-40 mr-16"></md-icon>
<span class="mat-display-2 m-0 font-weight-300 secondary-text">
{{widgets.weatherWidget.locations[widgets.weatherWidget.currentLocation].temp[widgets.weatherWidget.tempUnit]}}
</span>
<span class="font-size-48 font-weight-300 hint-text text-super ml-8">°</span>
<span class="mat-display-2 mb-0 font-weight-300 hint-text ng-binding">C</span>
</div>
</div>
<div class="grey-300-bg p-16" fxLayout="row" fxLayoutAlign="space-between center">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon fontSet="meteocons" fontIcon="icon-windy" class="mr-8 s-16"></md-icon>
<span>
{{widgets.weatherWidget.locations[widgets.weatherWidget.currentLocation].windSpeed[widgets.weatherWidget.speedUnit]}}
</span>
<span class="secondary-text ml-5">{{widgets.weatherWidget.speedUnit}}</span>
</div>
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon fontSet="meteocons" fontIcon="icon-compass" class="mr-8 s-16"></md-icon>
<span>{{widgets.weatherWidget.locations[widgets.weatherWidget.currentLocation].windDirection}}</span>
</div>
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon fontSet="meteocons" fontIcon="icon-rainy" class="mr-8 s-16"></md-icon>
<span>{{widgets.weatherWidget.locations[widgets.weatherWidget.currentLocation].rainProbability}}</span>
</div>
</div>
<div class="py-16">
<div class="py-16 px-24" fxLayout="row" fxLayoutAlign="space-between center"
*ngFor="let day of widgets.weatherWidget.locations[widgets.weatherWidget.currentLocation].next3Days">
<span class="h4">{{day.name}}</span>
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon fontSet="meteocons" [fontIcon]="day.icon" class="mr-16"></md-icon>
<span class="h2">{{day.temp[widgets.weatherWidget.tempUnit]}}</span>
<span class="h2 font-weight-300 secondary-text text-super">&deg;</span>
<span class="h2 font-weight-300 secondary-text">{{widgets.weatherWidget.tempUnit}}</span>
</div>
</div>
</div>
<md-divider></md-divider>
</div>
<!-- / Front -->
</fuse-widget>
<!-- / WEATHER WIDGET -->
</div>
<!-- / WIDGET GROUP -->
</div>
</md-sidenav>
<!-- / SIDENAV -->
</md-sidenav-container>
</div>

View File

@ -1,46 +0,0 @@
#dashboard-project {
md-sidenav-container {
.center {
> .header {
height: 160px;
min-height: 160px;
max-height: 160px;
.selected-project {
background: rgba(0, 0, 0, 0.12);
color: #FFFFFF;
padding: 8px 16px;
height: 40px;
line-height: 24px;
font-size: 16px;
}
.project-selector {
margin-left: 1px;
border-radius: 0;
background: rgba(0, 0, 0, 0.12);
md-icon {
color: #FFFFFF;
}
}
}
> .content {
.mat-tab-label-container {
padding: 0 24px;
}
}
}
.sidenav {
width: 250px !important;
min-width: 250px !important;
max-width: 250px !important;
}
}
}

View File

@ -1,173 +0,0 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ProjectsDashboardService } from './projects.service';
import * as shape from 'd3-shape';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { DataSource } from '@angular/cdk';
@Component({
selector : 'fuse-project',
templateUrl : './project.component.html',
styleUrls : ['./project.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseProjectComponent implements OnInit, OnDestroy
{
projects: any[];
selectedProject: any;
widgets: any;
widget5: any = {};
widget6: any = {};
widget7: any = {};
widget8: any = {};
widget9: any = {};
widget11: any = {};
dateNow = Date.now();
constructor(private projectsDashboardService: ProjectsDashboardService)
{
this.projects = this.projectsDashboardService.projects;
this.selectedProject = this.projects[0];
this.widgets = this.projectsDashboardService.widgets;
/**
* Widget 5
*/
this.widget5 = {
currentRange : 'TW',
xAxis : true,
yAxis : true,
gradient : false,
legend : false,
showXAxisLabel: false,
xAxisLabel : 'Days',
showYAxisLabel: false,
yAxisLabel : 'Isues',
scheme : {
domain: ['#42BFF7', '#C6ECFD', '#C7B42C', '#AAAAAA']
},
onSelect : (ev) => {
console.log(ev);
},
supporting : {
currentRange : '',
xAxis : false,
yAxis : false,
gradient : false,
legend : false,
showXAxisLabel: false,
xAxisLabel : 'Days',
showYAxisLabel: false,
yAxisLabel : 'Isues',
scheme : {
domain: ['#42BFF7', '#C6ECFD', '#C7B42C', '#AAAAAA']
},
curve : shape.curveBasis
}
};
/**
* Widget 6
*/
this.widget6 = {
currentRange : 'TW',
legend : false,
explodeSlices: false,
labels : true,
doughnut : true,
gradient : false,
scheme : {
domain: ['#f44336', '#9c27b0', '#03a9f4', '#e91e63']
},
onSelect : (ev) => {
console.log(ev);
}
};
/**
* Widget 7
*/
this.widget7 = {
currentRange: 'T'
};
/**
* Widget 8
*/
this.widget8 = {
legend : false,
explodeSlices: false,
labels : true,
doughnut : false,
gradient : false,
scheme : {
domain: ['#f44336', '#9c27b0', '#03a9f4', '#e91e63', '#ffc107']
},
onSelect : (ev) => {
console.log(ev);
}
};
/**
* Widget 9
*/
this.widget9 = {
currentRange : 'TW',
xAxis : false,
yAxis : false,
gradient : false,
legend : false,
showXAxisLabel: false,
xAxisLabel : 'Days',
showYAxisLabel: false,
yAxisLabel : 'Isues',
scheme : {
domain: ['#42BFF7', '#C6ECFD', '#C7B42C', '#AAAAAA']
},
curve : shape.curveBasis
};
setInterval(() => {
this.dateNow = Date.now();
}, 1000);
}
ngOnInit()
{
/**
* Widget 11
*/
this.widget11.onContactsChanged = new BehaviorSubject({});
this.widget11.onContactsChanged.next(this.widgets.widget11.table.rows);
this.widget11.dataSource = new FilesDataSource(this.widget11);
}
ngOnDestroy()
{
}
}
export class FilesDataSource extends DataSource<any>
{
constructor(private widget11)
{
super();
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<any[]>
{
return this.widget11.onContactsChanged;
}
disconnect()
{
}
}

View File

@ -1,36 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { FuseProjectComponent } from './project.component';
import { SharedModule } from '../../../../../core/modules/shared.module';
import { ProjectsDashboardService } from './projects.service';
import { FuseWidgetModule } from '../../../../../core/components/widget/widget.module';
import { NgxChartsModule } from '@swimlane/ngx-charts';
const routes: Routes = [
{
path : 'apps/dashboards/project',
component: FuseProjectComponent,
resolve : {
data: ProjectsDashboardService
}
}
];
@NgModule({
imports : [
SharedModule,
RouterModule.forChild(routes),
FuseWidgetModule,
NgxChartsModule
],
declarations: [
FuseProjectComponent
],
providers : [
ProjectsDashboardService
]
})
export class ProjectModule
{
}

View File

@ -1,62 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Http } from '@angular/http';
@Injectable()
export class ProjectsDashboardService implements Resolve<any>
{
projects: any[];
widgets: any[];
constructor(
private http: Http
)
{
}
/**
* Resolve
* @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any}
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
return new Promise((resolve, reject) => {
Promise.all([
this.getProjects(),
this.getWidgets()
]).then(
() => {
resolve();
},
reject
);
});
}
getProjects(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/projects-dashboard-projects')
.subscribe(response => {
this.projects = response.json().data;
resolve(response.json().data);
}, reject);
});
}
getWidgets(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/projects-dashboard-widgets')
.subscribe(response => {
this.widgets = response.json().data;
resolve(response.json().data);
}, reject);
});
}
}

View File

@ -1,58 +0,0 @@
<md-table #table [dataSource]="dataSource">
<!-- Type Column -->
<ng-container cdkColumnDef="icon">
<md-header-cell *cdkHeaderCellDef fxFlex="64px"></md-header-cell>
<md-cell *cdkCellDef="let row" fxFlex="64px">
<md-icon class="type-icon" [class]="row.type"></md-icon>
</md-cell>
</ng-container>
<!-- Name Column -->
<ng-container cdkColumnDef="name">
<md-header-cell *cdkHeaderCellDef>Name</md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.name}}</md-cell>
</ng-container>
<!-- Type Column -->
<ng-container cdkColumnDef="type">
<md-header-cell *cdkHeaderCellDef fxHide fxShow.gt-md>Type</md-header-cell>
<md-cell *cdkCellDef="let row" fxHide fxShow.gt-md> {{row.type}}</md-cell>
</ng-container>
<!-- Owner Column -->
<ng-container cdkColumnDef="owner">
<md-header-cell *cdkHeaderCellDef fxHide.xs>Owner</md-header-cell>
<md-cell *cdkCellDef="let row" fxHide.xs> {{row.owner}}</md-cell>
</ng-container>
<!-- Size Column -->
<ng-container cdkColumnDef="size">
<md-header-cell *cdkHeaderCellDef fxHide.xs>Size</md-header-cell>
<md-cell *cdkCellDef="let row" fxHide.xs>{{row.size === '' ? '-': row.size}}</md-cell>
</ng-container>
<!-- Modified Column -->
<ng-container cdkColumnDef="modified">
<md-header-cell *cdkHeaderCellDef fxHide fxShow.gt-md>Modified</md-header-cell>
<md-cell *cdkCellDef="let row" fxHide fxShow.gt-md>{{row.modified}}</md-cell>
</ng-container>
<!-- Detail Button Column -->
<ng-container cdkColumnDef="detail-button">
<md-header-cell *cdkHeaderCellDef fxFlex="48px" fxHide.gt-md></md-header-cell>
<md-cell *cdkCellDef="let row" fxFlex="48px" fxHide.gt-md>
<button md-icon-button class="sidenav-toggle"
fuseMdSidenavToggler="file-manager-right-sidenav">
<md-icon>info</md-icon>
</button>
</md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let row; columns: displayedColumns;"
(click)="onSelect(row)"
[ngClass]="{'md-light-blue-50-bg':row == selected}"
md-ripple>
</md-row>
</md-table>

View File

@ -1,24 +0,0 @@
@import "src/app/core/scss/fuse";
:host {
width: 100%;
.mat-table {
background: transparent;
box-shadow: none;
.mat-row {
position: relative;
cursor: pointer;
.mat-cell {
&.mat-column-icon {
}
&.mat-column-detail-button {
}
}
}
}
}

View File

@ -1,55 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { FileManagerService } from '../file-manager.service';
import { DataSource } from '@angular/cdk';
import { Observable } from 'rxjs/Observable';
@Component({
selector : 'fuse-file-list',
templateUrl: './file-list.component.html',
styleUrls : ['./file-list.component.scss']
})
export class FuseFileManagerFileListComponent implements OnInit
{
files: any;
dataSource: FilesDataSource | null;
displayedColumns = ['icon', 'name', 'type', 'owner', 'size', 'modified', 'detail-button'];
selected: any;
constructor(private fileManagerService: FileManagerService)
{
this.fileManagerService.onFilesChanged.subscribe(files => {
this.files = files;
});
this.fileManagerService.onFileSelected.subscribe(selected => {
this.selected = selected;
});
}
ngOnInit()
{
this.dataSource = new FilesDataSource(this.fileManagerService);
}
onSelect(selected)
{
this.fileManagerService.onFileSelected.next(selected);
}
}
export class FilesDataSource extends DataSource<any>
{
constructor(private fileManagerService: FileManagerService)
{
super();
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<any[]>
{
return this.fileManagerService.onFilesChanged;
}
disconnect()
{
}
}

View File

@ -1,77 +0,0 @@
<div id="file-manager" class="page-layout simple right-sidenav" perfect-scrollbar>
<md-sidenav-container>
<!-- SIDENAV -->
<md-sidenav class="sidenav left-sidenav" align="start" opened="false" mode="over"
fuseMdSidenavHelper="file-manager-left-sidenav">
<fuse-file-manager-main-sidenav></fuse-file-manager-main-sidenav>
</md-sidenav>
<!-- / SIDENAV -->
<!-- CENTER -->
<div class="center" fxFlex>
<!-- HEADER -->
<div class="header md-accent-bg p-24" fxLayout="column" fxLayoutAlign="space-between start">
<!-- TOOLBAR -->
<div class="toolbar w-100-p" fxFlex fxLayout="row" fxLayoutAlign="space-between start">
<div class="left-side" fxLayout="row">
<button md-icon-button class="sidenav-toggle"
fuseMdSidenavToggler="file-manager-left-sidenav">
<md-icon>menu</md-icon>
</button>
</div>
<div class="right-side" fxLayout="row">
<button md-button class="mat-icon-button" aria-label="Search" md-tooltip="Search">
<md-icon>search</md-icon>
</button>
</div>
</div>
<!-- / TOOLBAR -->
<!-- BREADCRUMB -->
<div class="breadcrumb text-truncate h1 pl-72" fxLayout="row" fxLayoutAlign="start center">
<div *ngFor="let path of pathArr; last as isLast" fxLayout="row" fxLayoutAlign="start center">
<span>{{path}}</span>
<md-icon *ngIf="!isLast" class="separator">chevron_right</md-icon>
</div>
</div>
<!-- / BREADCRUMB -->
<!-- ADD FILE BUTTON -->
<div class="file-uploader">
<input hidden type="file" #fileInput/>
<button md-fab
class="add-file-button mat-warn"
(click)="fileInput.click()"
aria-label="Add file">
<md-icon>add</md-icon>
</button>
</div>
<!-- / ADD FILE BUTTON -->
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content md-white-bg" perfect-scrollbar>
<fuse-file-list></fuse-file-list>
</div>
<!-- / CONTENT -->
</div>
<!-- / CENTER -->
<!-- SIDENAV -->
<md-sidenav class="sidenav right-sidenav mat-sidenav-opened" align="end" opened="true" mode="side"
fuseMdSidenavHelper="file-manager-right-sidenav" md-is-locked-open="gt-md">
<fuse-file-manager-details-sidenav></fuse-file-manager-details-sidenav>
</md-sidenav>
<!-- / SIDENAV -->
</md-sidenav-container>
</div>

View File

@ -1,76 +0,0 @@
@import "src/app/core/scss/fuse";
#file-manager {
md-sidenav-container {
.sidenav {
width: 320px !important;
min-width: 320px !important;
max-width: 320px !important;
&.left-sidenav {
}
&.right-sidenav {
@include media-breakpoint('gt-md') {
z-index: 0;
}
}
}
.mat-sidenav-content {
z-index: 1;
.center {
.header {
position: relative;
height: 160px;
min-height: 160px;
max-height: 160px;
@include media-breakpoint-down('sm'){
height: 120px;
min-height: 120px;
max-height: 120px;
}
.add-file-button {
position: absolute;
bottom: -28px;
left: 16px;
z-index: 999;
}
}
.content {
}
}
}
}
.type-icon {
&.folder {
&:before {
content: 'folder';
color: #FFB300;
}
}
&.document {
&:before {
content: 'insert_drive_file';
color: #1565C0;
}
}
&.spreadsheet {
&:before {
content: 'insert_chart';
color: #4CAF50;
}
}
}
}

View File

@ -1,29 +0,0 @@
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { FileManagerService } from './file-manager.service';
@Component({
selector : 'fuse-file-manager',
templateUrl : './file-manager.component.html',
styleUrls : ['./file-manager.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseFileManagerComponent implements OnInit
{
selected: any;
pathArr: string[];
constructor(private fileManagerService: FileManagerService)
{
}
ngOnInit()
{
this.fileManagerService.onFileSelected.subscribe(selected => {
this.selected = selected;
this.pathArr = selected.location.split('>');
});
}
}

View File

@ -1,38 +0,0 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../../../../core/modules/shared.module';
import { RouterModule, Routes } from '@angular/router';
import { FuseFileManagerComponent } from './file-manager.component';
import { FileManagerService } from './file-manager.service';
import { FuseFileManagerFileListComponent } from './file-list/file-list.component';
import { FuseFileManagerMainSidenavComponent } from './sidenavs/main/main.component';
import { FuseFileManagerDetailsSidenavComponent } from './sidenavs/details/details.component';
const routes: Routes = [
{
path : '**',
component: FuseFileManagerComponent,
children : [],
resolve : {
files: FileManagerService
}
}
];
@NgModule({
imports : [
SharedModule,
RouterModule.forChild(routes)
],
declarations: [
FuseFileManagerComponent,
FuseFileManagerFileListComponent,
FuseFileManagerMainSidenavComponent,
FuseFileManagerDetailsSidenavComponent
],
providers : [
FileManagerService
]
})
export class FuseFileManagerModule
{
}

View File

@ -1,51 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Http } from '@angular/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class FileManagerService implements Resolve<any>
{
onFilesChanged: BehaviorSubject<any> = new BehaviorSubject({});
onFileSelected: BehaviorSubject<any> = new BehaviorSubject({});
constructor(private http: Http)
{
}
/**
* The File Manager App Main Resolver
* @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any}
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
return new Promise((resolve, reject) => {
Promise.all([
this.getFiles()
]).then(
([files]) => {
resolve();
},
reject
);
});
}
getFiles(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/file-manager')
.subscribe(response => {
this.onFilesChanged.next(response.json().data);
this.onFileSelected.next(response.json().data[0]);
resolve(response.json().data);
}, reject);
});
}
}

View File

@ -1,83 +0,0 @@
<!-- SIDENAV HEADER -->
<div class="header md-accent-bg p-24" fxLayout="column" fxLayoutAlign="space-between">
<div class="toolbar" fxLayout="row" fxLayoutAlign="end center">
<button md-icon-button class="mat-icon-button" md-tooltip="Delete">
<md-icon>delete</md-icon>
</button>
<button md-icon-button class="" aria-label="Download" md-tooltip="Download">
<md-icon>file_download</md-icon>
</button>
<button md-icon-button class="mat-icon-button" aria-label="More" md-tooltip="More">
<md-icon>more_vert</md-icon>
</button>
</div>
<div>
<div class="title mb-8">{{selected.name}}</div>
<div class="subtitle secondary-text">
<span>Edited</span>
<span>: {{selected.modified}}</span>
</div>
</div>
</div>
<!-- / SIDENAV HEADER -->
<!-- SIDENAV CONTENT -->
<div class="content p-24 md-white-bg" perfect-scrollbar>
<div class="file-details">
<div class="preview file-icon" fxLayout="row" fxLayoutAlign="center center">
<md-icon class="type-icon s-48" [class]="selected.type"></md-icon>
</div>
<div class="offline-switch">
<md-slide-toggle ([ngModel])="selected.offline" labelPosition="before">Available Offline</md-slide-toggle>
</div>
<div class="title">Info</div>
<table>
<tr class="type">
<th>Type</th>
<td>{{selected.type}}</td>
</tr>
<tr class="size">
<th>Size</th>
<td>{{selected.size === '' ? '-': selected.size}}</td>
</tr>
<tr class="location">
<th>Location</th>
<td>{{selected.location}}</td>
</tr>
<tr class="owner">
<th>Owner</th>
<td>{{selected.owner}}</td>
</tr>
<tr class="modified">
<th>Modified</th>
<td>{{selected.modified}}</td>
</tr>
<tr class="opened">
<th>Opened</th>
<td>{{selected.opened}}</td>
</tr>
<tr class="created">
<th>Created</th>
<td>{{selected.created}}</td>
</tr>
</table>
</div>
</div>
<!-- / SIDENAV CONTENT -->

View File

@ -1,73 +0,0 @@
:host {
display: flex;
flex-direction: column;
flex: 1 0 auto;
height: 100%;
> .header {
flex: 0 1 auto;
height: 160px;
min-height: 160px;
max-height: 160px;
}
> .content {
flex: 1;
.file-details {
.preview {
height: 240px;
}
.offline-switch {
padding-bottom: 16px;
font-weight: 500;
}
.title {
padding: 16px 0;
}
table {
width: 100%;
text-align: left;
tr {
th, td {
padding: 16px 0;
}
th {
}
td {
}
&.type {
text-transform: capitalize;
}
&.size {
}
&.location {
}
&.owner {
text-transform: capitalize;
}
&.opened {
}
&.created {
}
}
}
}
}
}

View File

@ -1,26 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { FileManagerService } from '../../file-manager.service';
@Component({
selector : 'fuse-file-manager-details-sidenav',
templateUrl: './details.component.html',
styleUrls : ['./details.component.scss']
})
export class FuseFileManagerDetailsSidenavComponent implements OnInit
{
selected: any;
constructor(private fileManagerService: FileManagerService)
{
}
ngOnInit()
{
this.fileManagerService.onFileSelected.subscribe(selected => {
this.selected = selected;
});
}
}

View File

@ -1,54 +0,0 @@
<!-- SIDENAV HEADER -->
<div class="header p-24" fxLayout="column" fxLayoutAlign="space-between">
<div class="logo" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="logo-icon mr-16">folder</md-icon>
<span class="logo-text h1">File Manager</span>
</div>
</div>
<!-- / SIDENAV HEADER -->
<!-- SIDENAV CONTENT -->
<div class="content py-16" perfect-scrollbar>
<div class="nav">
<div class="nav-item" aria-label="inbox">
<a class="nav-link" md-ripple>
<md-icon class="nav-link-icon">folder</md-icon>
<span class="title">My Files</span>
</a>
</div>
<div class="nav-item" aria-label="starred">
<a class="nav-link" md-ripple>
<md-icon class="nav-link-icon">star</md-icon>
<div class="title">Starred</div>
</a>
</div>
<div class="nav-item" aria-label="starred">
<a class="nav-link" md-ripple>
<md-icon class="nav-link-icon">folder_shared</md-icon>
<div class="title">Sharred with me</div>
</a>
</div>
<div class="nav-item" aria-label="starred">
<a class="nav-link" md-ripple>
<md-icon class="nav-link-icon">access_time</md-icon>
<div class="title">Recent</div>
</a>
</div>
<div class="nav-item" aria-label="starred">
<a class="nav-link" md-ripple>
<md-icon class="nav-link-icon">not_interested</md-icon>
<div class="title">Offline</div>
</a>
</div>
</div>
</div>
<!-- / SIDENAV CONTENT -->

View File

@ -1,11 +0,0 @@
:host {
display: flex;
flex-direction: column;
flex: 1 0 auto;
height: 100%;
> .header {
flex: 0 1 auto;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
}

View File

@ -1,21 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector : 'fuse-file-manager-main-sidenav',
templateUrl: './main.component.html',
styleUrls : ['./main.component.scss']
})
export class FuseFileManagerMainSidenavComponent implements OnInit
{
selected: any;
constructor()
{
}
ngOnInit()
{
}
}

View File

@ -1,108 +0,0 @@
<md-toolbar md-dialog-title class="mat-accent m-0">
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<span class="title dialog-title">New Message</span>
<button md-button class="mat-icon-button"
(click)="dialogRef.close()"
aria-label="Close dialog">
<md-icon>close</md-icon>
</button>
</div>
</md-toolbar>
<div md-dialog-content class="p-24 m-0" perfect-scrollbar>
<form name="composeForm" [formGroup]="composeForm" class="compose-form" fxLayout="column" fxFlex>
<md-input-container>
<input mdInput name="from"
placeholder="From"
formControlName="from"
type="email">
</md-input-container>
<md-input-container ng-class="{'hidden-cc': vm.hiddenCC, 'hidden-bcc': vm.hiddenBCC}">
<input mdInput name="to"
placeholder="To"
formControlName="to"
type="email" required>
</md-input-container>
<md-input-container>
<input mdInput
name="cc"
placeholder="Cc"
formControlName="cc"
type="email">
</md-input-container>
<md-input-container>
<input mdInput
name="bcc"
placeholder="Bcc"
formControlName="bcc"
type="email">
</md-input-container>
<md-input-container>
<input mdInput name="subject"
placeholder="Subject"
formControlName="subject">
</md-input-container>
<md-input-container>
<textarea mdInput name="message"
placeholder="Message"
formControlName="message">
</textarea>
</md-input-container>
<div class="attachment-list">
<div class="attachment" fxLayout="row" fxLayoutAlign="space-between center">
<div>
<span class="filename">attachment-2.doc</span>
<span class="size">(12 Kb)</span>
</div>
<button md-icon-button aria-label="Delete attachment">
<md-icon class="s-16">close</md-icon>
</button>
</div>
<div class="attachment" fxLayout="row" fxLayoutAlign="space-between center">
<div>
<span class="filename">attachment-1.jpg</span>
<span class="size">(350 Kb)</span>
</div>
<button md-icon-button aria-label="Delete attachment">
<md-icon class="s-16">close</md-icon>
</button>
</div>
</div>
</form>
</div>
<div md-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="space-between center">
<div>
<button md-raised-button
(click)="dialogRef.close(['send',composeForm])"
class="save-button mat-accent"
[disabled]="composeForm.invalid"
aria-label="SAVE">
SEND
</button>
<button md-icon-button md-tooltip="Attach a file">
<md-icon>attach_file</md-icon>
</button>
</div>
<button md-button
class="mat-icon-button"
(click)="dialogRef.close(['delete',composeForm])"
aria-label="Delete"
md-tooltip="Delete">
<md-icon>delete</md-icon>
</button>
</div>

View File

@ -1,32 +0,0 @@
:host {
display: flex;
flex-direction: column;
}
.mail-compose-dialog {
.mat-dialog-container {
padding: 0;
width: 720px;
.attachment-list {
font-size: 13px;
padding-top: 16px;
.attachment {
background-color: rgba(0, 0, 0, 0.08);
border: 1px solid rgba(0, 0, 0, 0.16);
padding-left: 16px;
margin-top: 8px;
border-radius: 2px;
.filename {
font-weight: 500;
}
&:last-child {
margin-bottom: 0;
}
}
}
}
}

View File

@ -1,42 +0,0 @@
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { MD_DIALOG_DATA, MdDialogRef } from '@angular/material';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector : 'fuse-mail-compose',
templateUrl : './compose.component.html',
styleUrls : ['./compose.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseMailComposeDialogComponent implements OnInit
{
composeForm: FormGroup;
constructor(
public dialogRef: MdDialogRef<FuseMailComposeDialogComponent>,
@Inject(MD_DIALOG_DATA) private data: any
)
{
this.composeForm = this.createComposeForm();
}
ngOnInit()
{
}
createComposeForm()
{
return new FormGroup({
from : new FormControl({
value : 'johndoe@creapond.com',
disabled: true
}),
to : new FormControl(''),
cc : new FormControl(''),
bcc : new FormControl(''),
subject: new FormControl(''),
message: new FormControl('')
});
}
}

View File

@ -1,137 +0,0 @@
<div *ngIf="!mail" fxLayout="column" fxLayoutAlign="center center" fxFlex>
<md-icon class="s-128 mb-16">email</md-icon>
<span class="select-message-text hint-text">Select a message to read</span>
</div>
<div *ngIf="mail">
<div class="mail-header" fxLayout="row" fxLayoutAlign="space-between center">
<div>
<div class="subject" flex>{{mail.subject}}</div>
<div class="labels" fxLayout="row" fxLayoutWrap>
<div class="label" *ngFor="let labelId of mail.labels"
fxLayout="row" fxLayoutAlign="start center">
<div class="label-color" [ngStyle]="{'background-color': labels | getById:labelId:'color'}"></div>
<div class="label-title">{{labels | getById:labelId:'title'}}</div>
</div>
</div>
</div>
<div class="actions" fxLayout="row" fxLayoutAlign="start center">
<button md-button class="mat-icon-button" (click)="toggleStar($event)" aria-label="Toggle star">
<md-icon *ngIf="mail.starred">star</md-icon>
<md-icon *ngIf="!mail.starred">star_outline</md-icon>
</button>
<button md-button class="mat-icon-button" (click)="toggleImportant($event)" aria-label="Toggle important">
<md-icon *ngIf="mail.important">label</md-icon>
<md-icon *ngIf="!mail.important">label_outline</md-icon>
</button>
</div>
</div>
<div class="mail-content">
<div class="info" fxLayout="row" fxLayoutAlign="space-between start">
<div fxFlex fxLayout="column" fxLayoutAlign="start start">
<div fxLayout="row" fxLayoutAlign="start start">
<div>
<img *ngIf="mail.from.avatar" alt="{{mail.from.name}}"
src="{{mail.from.avatar}}" class="avatar"/>
<div *ngIf="!mail.from.avatar" class="avatar" ms-random-class="vm.colors">
{{mail.from.name[0]}}
</div>
</div>
<div fxLayout="column" fxLayoutAlign="start start">
<div class="name">
{{mail.from.name}}
</div>
<div class="to" fxLayout="row" fxLayoutAlign="start center">
<div class="to-text">to</div>
<div>{{mail.to[0].name}}</div>
</div>
</div>
</div>
<a class="toggle-details" (click)="showDetails = !showDetails">
<span *ngIf="!showDetails">Show Details</span>
<span *ngIf="showDetails">Hide Details</span>
</a>
<div *ngIf="showDetails" class="details" fxLayout="row" fxLayoutAlign="start start">
<div fxLayout="column">
<span class="title">From:</span>
<span class="title">To:</span>
<span class="title">Date:</span>
</div>
<div fxLayout="column">
<span class="detail">{{mail.from.email}}</span>
<span class="detail">{{mail.to[0].email}}</span>
<span class="detail">{{mail.time}}</span>
</div>
</div>
</div>
<button md-button [mdMenuTriggerFor]="moreMenu" aria-label="More" class="mat-icon-button"
ng-click="$mdOpenMenu($event)">
<md-icon>more_vert</md-icon>
</button>
<md-menu #moreMenu="mdMenu">
<button md-menu-item aria-label="Reply">
<md-icon>reply</md-icon>
<span>Reply</span>
</button>
<button md-menu-item aria-label="Forward">
<md-icon>forward</md-icon>
<span>Forward</span>
</button>
<button md-menu-item aria-label="Print">
<md-icon>print</md-icon>
<span>Print</span>
</button>
</md-menu>
</div>
<div [innerHTML]="mail.message"></div>
</div>
<div *ngIf="mail.attachments" class="mail-attachments">
<div class="title">
<span>Attachments</span>
({{mail.attachments.length}})
</div>
<div class="attachment-list" fxLayout="row" fxLayoutWrap>
<div class="attachment" fxLayout="column"
*ngFor="let attachment of mail.attachments">
<img class="preview" src="{{attachment.preview}}">
<div fxLayout="column">
<a href="#" onclick="event.preventDefault()">View</a>
<a href="#" onclick="event.preventDefault()">Download</a>
<div class="size">({{attachment.size}})</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,118 +0,0 @@
@import 'src/app/core/scss/fuse';
:host {
display: flex;
flex-direction: column;
flex: 1;
overflow-y: auto;
padding: 24px;
.select-message-text {
font-size: 24px;
font-weight: 300;
}
.mail-header {
padding-bottom: 16px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
.actions {
min-width: 88px;
}
.subject {
font-size: 17px;
font-weight: 500;
}
.label {
font-size: 11px;
border-radius: 2px;
margin: 4px 4px 4px 0;
padding: 3px 8px;
background-color: rgba(0, 0, 0, 0.08);
.label-color {
width: 8px;
height: 8px;
margin-right: 8px;
border-radius: 50%;
}
}
}
.mail-content {
padding: 24px 0;
.to {
color: rgba(0, 0, 0, 0.54);
.to-text {
margin-right: 4px;
text-transform: lowercase;
}
}
.info {
padding-bottom: 16px;
.avatar {
margin-right: 16px;
background-color: mat-color($accent);
}
.name {
margin-right: 8px;
font-weight: 500;
}
.toggle-details {
user-select: none;
text-decoration: underline;
padding-top: 16px;
cursor: pointer;
font-weight: 500;
}
.details {
padding-top: 8px;
.title {
font-weight: 500;
margin-right: 6px;
}
.detail {
color: rgba(0, 0, 0, 0.54);
}
}
}
}
.mail-attachments {
padding: 24px 0;
border-top: 1px solid rgba(0, 0, 0, 0.12);
.title {
margin-bottom: 16px;
font-weight: 500;
}
.attachment {
.preview {
width: 100px;
margin: 0 16px 8px 0;
}
.link {
margin-bottom: 2px;
}
.size {
font-size: 11px;
}
}
}
}

View File

@ -1,66 +0,0 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MailService } from '../mail.service';
import { Mail } from '../mail.model';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector : 'fuse-mail-details',
templateUrl: './mail-details.component.html',
styleUrls : ['./mail-details.component.scss']
})
export class FuseMailDetailsComponent implements OnInit, OnDestroy
{
mail: Mail;
labels: any[];
showDetails = false;
onCurrentMailChanged: Subscription;
onLabelsChanged: Subscription;
constructor(
private mailService: MailService
)
{
}
ngOnInit()
{
// Subscribe to update the current mail
this.onCurrentMailChanged =
this.mailService.onCurrentMailChanged
.subscribe(currentMail => {
this.mail = currentMail;
});
// Subscribe to update on label change
this.onLabelsChanged =
this.mailService.onLabelsChanged
.subscribe(labels => {
this.labels = labels;
});
}
ngOnDestroy()
{
this.onCurrentMailChanged.unsubscribe();
}
toggleStar(event)
{
event.stopPropagation();
this.mail.toggleStar();
this.mailService.updateMail(this.mail);
}
toggleImportant(event)
{
event.stopPropagation();
this.mail.toggleImportant();
this.mailService.updateMail(this.mail);
}
}

View File

@ -1,52 +0,0 @@
<div fxLayout="row" fxLayoutAlign="start center">
<md-checkbox [(ngModel)]="selected" (ngModelChange)="onSelectedChange()" (click)="$event.stopPropagation();"></md-checkbox>
<div class="info" fxFlex FlexLayout="column">
<div class="name" fxLayout="row" fxLayoutAlign="start center">
<img class="avatar" *ngIf="mail.from?.avatar" alt="{{mail.from?.name}}" src="{{mail.from?.avatar}}"/>
<div class="avatar" *ngIf="!mail.from?.avatar">{{mail.from?.name[0]}}</div>
<span class="text-truncate" *ngIf="mail?.from">{{mail.from?.name}}</span>
<md-icon *ngIf="mail.hasAttachments">attachment</md-icon>
</div>
<div class="subject text-truncate">
{{mail.subject}}
</div>
<div class="message text-truncate" *ngIf="mail?.message">
{{mail.message | htmlToPlaintext | slice:0:180}}{{mail.message.length > 180 ? '...' : ''}}
</div>
<div class="labels" fxLayout="row" fxLayoutWrap>
<div class="label" *ngFor="let labelId of mail.labels"
fxLayout="row" fxLayoutAlign="start center">
<div class="label-color" [ngStyle]="{'background-color': labels | getById:labelId:'color'}"></div>
<div class="label-title">{{labels | getById:labelId:'title'}}</div>
</div>
</div>
</div>
<div fxLayout="column" fxLayoutAlign="space-between end">
<div class="time">{{mail.time}}</div>
<div class="actions" fxLayout="row" fxLayoutAlign="start center">
<button md-button class="mat-icon-button" (click)="toggleStar($event)" aria-label="Toggle star">
<md-icon *ngIf="mail.starred">star</md-icon>
<md-icon *ngIf="!mail.starred">star_outline</md-icon>
</button>
<button md-button class="mat-icon-button" (click)="toggleImportant($event)" aria-label="Toggle important">
<md-icon *ngIf="mail.important">label</md-icon>
<md-icon *ngIf="!mail.important">label_outline</md-icon>
</button>
</div>
</div>
</div>

View File

@ -1,124 +0,0 @@
@import 'src/app/core/scss/fuse';
:host {
flex-shrink: 0;
position: relative;
padding: 16px 24px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
cursor: pointer;
&.unread {
background: #FFFFFF;
.info {
.name,
.subject {
font-weight: 700;
}
.message {
}
.labels {
background: #FFFFFF;
}
}
}
&.selected {
background: #FFF8E1;
.info {
.message {
}
.labels {
background: #FFF8E1;
}
}
}
&.current-mail {
background: #E3F2FD;
.info {
.message {
}
.labels {
background: #E3F2FD;
}
}
}
.info {
overflow: hidden;
width: 0;
margin: 0 16px;
position: relative;
.name {
font-size: 15px;
font-weight: 500;
padding-bottom: 8px;
.avatar {
min-width: 32px;
width: 32px;
height: 32px;
line-height: 32px;
background-color: mat-color($accent);
}
}
.subject {
}
.message {
position: relative;
color: rgba(0, 0, 0, 0.54);
}
.labels {
position: absolute;
background: #FAFAFA;
bottom: 0;
right: 0;
padding-left: 6px;
.label {
font-size: 11px;
border-radius: 2px;
margin: 0 4px 0 0;
padding: 3px 8px;
background-color: rgba(0, 0, 0, 0.08);
.label-color {
width: 8px;
height: 8px;
margin-right: 8px;
border-radius: 50%;
}
}
}
}
.time {
margin: 0 8px 6px 8px;
}
.actions {
.md-icon-button {
margin: 0;
padding: 0;
width: 32px;
height: 32px;
}
}
}

View File

@ -1,93 +0,0 @@
import { Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
import { Mail } from '../../mail.model';
import { MailService } from '../../mail.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector : 'fuse-mail-list-item',
templateUrl: './mail-list-item.component.html',
styleUrls : ['./mail-list-item.component.scss']
})
export class FuseMailListItemComponent implements OnInit, OnDestroy
{
@Input() mail: Mail;
labels: any[];
@HostBinding('class.selected') selected: boolean;
onSelectedMailsChanged: Subscription;
onLabelsChanged: Subscription;
constructor(
private mailService: MailService
)
{
}
ngOnInit()
{
// Set the initial values
this.mail = new Mail(this.mail);
// Subscribe to update on selected mail change
this.onSelectedMailsChanged =
this.mailService.onSelectedMailsChanged
.subscribe(selectedMails => {
this.selected = false;
if ( selectedMails.length > 0 )
{
for ( const mail of selectedMails )
{
if ( mail.id === this.mail.id )
{
this.selected = true;
break;
}
}
}
});
// Subscribe to update on label change
this.onLabelsChanged =
this.mailService.onLabelsChanged
.subscribe(labels => {
this.labels = labels;
});
}
ngOnDestroy()
{
this.onSelectedMailsChanged.unsubscribe();
}
onSelectedChange()
{
this.mailService.toggleSelectedMail(this.mail.id);
}
/**
* Toggle star
* @param event
*/
toggleStar(event)
{
event.stopPropagation();
this.mail.toggleStar();
this.mailService.updateMail(this.mail);
}
/**
* Toggle Important
* @param event
*/
toggleImportant(event)
{
event.stopPropagation();
this.mail.toggleImportant();
this.mailService.updateMail(this.mail);
}
}

View File

@ -1,7 +0,0 @@
<div *ngIf="mails.length === 0" fxLayout="column" fxLayoutAlign="center center" fxFlex>
<span class="no-messages-text hint-text">There are no messages!</span>
</div>
<fuse-mail-list-item md-ripple *ngFor="let mail of mails" [mail]="mail" (click)="readMail(mail.id)"
[ngClass]="{'current-mail':mail?.id == currentMail?.id}">
</fuse-mail-list-item>

View File

@ -1,14 +0,0 @@
:host {
position: relative;
display: flex;
flex-direction: column;
flex: 1;
overflow-y: auto;
padding: 0;
border-right: 1px solid rgba(0, 0, 0, .12);
.no-messages-text {
font-size: 24px;
font-weight: 300;
}
}

View File

@ -1,105 +0,0 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Mail } from '../mail.model';
import { ActivatedRoute } from '@angular/router';
import { MailService } from '../mail.service';
import { Location } from '@angular/common';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector : 'fuse-mail-list',
templateUrl: './mail-list.component.html',
styleUrls : ['./mail-list.component.scss']
})
export class FuseMailListComponent implements OnInit, OnDestroy
{
mails: Mail[];
currentMail: Mail;
onMailsChanged: Subscription;
onCurrentMailChanged: Subscription;
constructor(
private route: ActivatedRoute,
private mailService: MailService,
private location: Location
)
{
}
ngOnInit()
{
// Subscribe to update mails on changes
this.onMailsChanged =
this.mailService.onMailsChanged
.subscribe(mails => {
this.mails = mails;
});
// Subscribe to update current mail on changes
this.onCurrentMailChanged =
this.mailService.onCurrentMailChanged
.subscribe(currentMail => {
if ( !currentMail )
{
// Set the current mail id to null to deselect the current mail
this.currentMail = null;
// Handle the location changes
const labelHandle = this.route.snapshot.params.labelHandle,
filterHandle = this.route.snapshot.params.filterHandle,
folderHandle = this.route.snapshot.params.folderHandle;
if ( labelHandle )
{
this.location.go('apps/mail/label/' + labelHandle);
}
else if ( filterHandle )
{
this.location.go('apps/mail/filter/' + filterHandle);
}
else
{
this.location.go('apps/mail/' + folderHandle);
}
}
else
{
this.currentMail = currentMail;
}
});
}
ngOnDestroy()
{
this.onMailsChanged.unsubscribe();
this.onCurrentMailChanged.unsubscribe();
}
/**
* Read mail
* @param mailId
*/
readMail(mailId)
{
const labelHandle = this.route.snapshot.params.labelHandle,
filterHandle = this.route.snapshot.params.filterHandle,
folderHandle = this.route.snapshot.params.folderHandle;
if ( labelHandle )
{
this.location.go('apps/mail/label/' + labelHandle + '/' + mailId);
}
else if ( filterHandle )
{
this.location.go('apps/mail/filter/' + filterHandle + '/' + mailId);
}
else
{
this.location.go('apps/mail/' + folderHandle + '/' + mailId);
}
// Set current mail
this.mailService.setCurrentMail(mailId);
}
}

View File

@ -1,114 +0,0 @@
<div id="mail" class="page-layout carded left-sidenav" perfect-scrollbar>
<!-- TOP BACKGROUND -->
<div class="top-bg md-accent-bg"></div>
<!-- / TOP BACKGROUND -->
<md-sidenav-container>
<!-- SIDENAV -->
<md-sidenav class="sidenav mat-sidenav-opened" align="start" opened="true" mode="side"
fuseMdSidenavHelper="carded-left-sidenav" md-is-locked-open="gt-md">
<fuse-mail-main-sidenav></fuse-mail-main-sidenav>
</md-sidenav>
<!-- / SIDENAV -->
<!-- CENTER -->
<div class="center">
<!-- CONTENT HEADER -->
<div class="header" fxLayout="row" fxLayoutAlign="start center">
<div class="search-wrapper" fxFlex fxLayout="row" fxLayoutAlign="start center">
<button md-button class="mat-icon-button sidenav-toggle"
fuseMdSidenavToggler="carded-left-sidenav"
fxHide.gt-md aria-label="Toggle Sidenav">
<md-icon>menu</md-icon>
</button>
<div class="search md-white-bg" flex fxLayout="row" fxLayoutAlign="start center">
<md-icon>search</md-icon>
<input [formControl]="searchInput" placeholder="Search for an e-mail or contact" fxFlex>
</div>
</div>
</div>
<!-- / CONTENT HEADER -->
<!-- CONTENT CARD -->
<div class="content-card md-white-bg" [ngClass]="{'current-mail-selected':currentMail}">
<!-- CONTENT TOOLBAR -->
<div class="toolbar px-24 py-8">
<div fxFlex="row" fxLayoutAlign="start center">
<md-checkbox (click)="toggleSelectAll()" [checked]="hasSelectedMails"
[indeterminate]="isIndeterminate"></md-checkbox>
<button md-icon-button [mdMenuTriggerFor]="selectMenu">
<md-icon>arrow_drop_down</md-icon>
</button>
<md-menu #selectMenu="mdMenu">
<button md-menu-item (click)="selectMails()">All</button>
<button md-menu-item (click)="deselectMails()">None</button>
<button md-menu-item (click)="selectMails('read', true)">Read</button>
<button md-menu-item (click)="selectMails('read', false)">Unread</button>
<button md-menu-item (click)="selectMails('starred', true)">Starred</button>
<button md-menu-item (click)="selectMails('starred', false)">Unstarred</button>
<button md-menu-item (click)="selectMails('important', true)">Important</button>
<button md-menu-item (click)="selectMails('important', false)">Unimportant</button>
</md-menu>
<div class="toolbar-separator" *ngIf="hasSelectedMails"></div>
<button md-icon-button (click)="setFolderOnSelectedMails(4)" *ngIf="hasSelectedMails">
<md-icon>delete</md-icon>
</button>
<button md-icon-button [mdMenuTriggerFor]="folderMenu" *ngIf="hasSelectedMails">
<md-icon>folder</md-icon>
</button>
<md-menu #folderMenu="mdMenu">
<button md-menu-item *ngFor="let folder of folders"
(click)="setFolderOnSelectedMails(folder.id)">{{folder.title}}
</button>
</md-menu>
<button md-icon-button [mdMenuTriggerFor]="labelMenu" *ngIf="hasSelectedMails">
<md-icon>label</md-icon>
</button>
<md-menu #labelMenu="mdMenu">
<button md-menu-item *ngFor="let label of labels"
(click)="toggleLabelOnSelectedMails(label.id)">{{label.title}}
</button>
</md-menu>
</div>
<div *ngIf="currentMail" fxHide.gt-lg>
<button md-icon-button (click)="deSelectCurrentMail()">
<md-icon>arrow_back</md-icon>
</button>
</div>
</div>
<!-- / CONTENT TOOLBAR -->
<!-- CONTENT -->
<div class="content" fxLayoutAlign="row">
<fuse-mail-list perfect-scrollbar fxFlex></fuse-mail-list>
<fuse-mail-details perfect-scrollbar fxFlex></fuse-mail-details>
</div>
<!-- / CONTENT -->
</div>
<!-- / CONTENT CARD -->
</div>
<!-- / CENTER -->
</md-sidenav-container>
</div>

View File

@ -1,69 +0,0 @@
@import "src/app/core/scss/fuse";
:host {
width: 100%;
.center {
.header {
.search-wrapper {
@include mat-elevation(7);
.sidenav-toggle {
margin: 0;
width: 56px;
height: 56px;
background: #FFF;
border-radius: 0;
border-right: 1px solid rgba(0, 0, 0, .12);
}
.search {
width: 100%;
height: 56px;
line-height: 56px;
padding: 18px;
input {
height: 56px;
padding-left: 16px;
color: rgba(0, 0, 0, 0.54);
border: none;
outline: none;
}
}
}
}
.content-card {
@include media-breakpoint(xs) {
fuse-mail-list,
fuse-mail-details {
flex: 1 0 100%;
}
fuse-mail-details {
display: none !important;
}
&.current-mail-selected {
.content {
fuse-mail-list {
display: none !important;
}
fuse-mail-details {
display: flex !important;
}
}
}
}
}
}
}

View File

@ -1,123 +0,0 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MailService } from './mail.service';
import { Subscription } from 'rxjs/Subscription';
import { FormControl } from '@angular/forms';
import { Mail } from './mail.model';
@Component({
selector : 'fuse-mail',
templateUrl: './mail.component.html',
styleUrls : ['./mail.component.scss']
})
export class FuseMailComponent implements OnInit, OnDestroy
{
hasSelectedMails: boolean;
isIndeterminate: boolean;
folders: any[];
filters: any[];
labels: any[];
searchInput: FormControl;
currentMail: Mail;
onSelectedMailsChanged: Subscription;
onFoldersChanged: Subscription;
onFiltersChanged: Subscription;
onLabelsChanged: Subscription;
onCurrentMailChanged: Subscription;
constructor(private mailService: MailService)
{
this.searchInput = new FormControl('');
}
ngOnInit()
{
this.onSelectedMailsChanged =
this.mailService.onSelectedMailsChanged
.subscribe(selectedMails => {
setTimeout(() => {
this.hasSelectedMails = selectedMails.length > 0;
this.isIndeterminate = (selectedMails.length !== this.mailService.mails.length && selectedMails.length > 0);
}, 0);
});
this.onFoldersChanged =
this.mailService.onFoldersChanged
.subscribe(folders => {
this.folders = this.mailService.folders;
});
this.onFiltersChanged =
this.mailService.onFiltersChanged
.subscribe(folders => {
this.filters = this.mailService.filters;
});
this.onLabelsChanged =
this.mailService.onLabelsChanged
.subscribe(labels => {
this.labels = this.mailService.labels;
});
this.onCurrentMailChanged =
this.mailService.onCurrentMailChanged
.subscribe(currentMail => {
if ( !currentMail )
{
this.currentMail = null;
}
else
{
this.currentMail = currentMail;
}
});
/*this.searchInput.valueChanges
.debounceTime(300)
.distinctUntilChanged()
.subscribe(searchText => {
this.mailService.onSearchTextChanged.next(searchText);
});*/
}
ngOnDestroy()
{
this.onSelectedMailsChanged.unsubscribe();
this.onFoldersChanged.unsubscribe();
this.onFiltersChanged.unsubscribe();
this.onLabelsChanged.unsubscribe();
this.onCurrentMailChanged.unsubscribe();
}
toggleSelectAll()
{
this.mailService.toggleSelectAll();
}
selectMails(filterParameter?, filterValue?)
{
this.mailService.selectMails(filterParameter, filterValue);
}
deselectMails()
{
this.mailService.deselectMails();
}
deSelectCurrentMail()
{
this.mailService.onCurrentMailChanged.next(null);
}
toggleLabelOnSelectedMails(labelId)
{
this.mailService.toggleLabelOnSelectedMails(labelId);
}
setFolderOnSelectedMails(folderId)
{
this.mailService.setFolderOnSelectedMails(folderId);
}
}

View File

@ -1,56 +0,0 @@
export class Mail
{
id: string;
from: {
name: string,
avatar: string,
email: string
};
to: {
name: string,
email: string
}[];
subject: string;
message: string;
time: string;
read: boolean;
starred: boolean;
important: boolean;
hasAttachments: boolean;
attachments: {
type: string,
fileName: string,
preview: string,
url: string,
size: string
}[];
labels: string[];
folder: string;
constructor(mail)
{
this.id = mail.id;
this.from = mail.from;
this.to = mail.to;
this.subject = mail.subject;
this.message = mail.message;
this.time = mail.time;
this.read = mail.read;
this.starred = mail.starred;
this.important = mail.important;
this.hasAttachments = mail.hasAttachments;
this.attachments = mail.attachments;
this.labels = mail.labels;
this.folder = mail.folder;
}
toggleStar()
{
this.starred = !this.starred;
}
toggleImportant()
{
this.important = !this.important;
}
}

View File

@ -1,81 +0,0 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../../../../core/modules/shared.module';
import { RouterModule, Routes } from '@angular/router';
import { FuseMailComponent } from './mail.component';
import { FuseMailMainSidenavComponent } from './sidenavs/main/main-sidenav.component';
import { FuseMailListItemComponent } from './mail-list/mail-list-item/mail-list-item.component';
import { FuseMailListComponent } from './mail-list/mail-list.component';
import { FuseMailDetailsComponent } from './mail-details/mail-details.component';
import { MailService } from './mail.service';
import { FuseMailComposeDialogComponent } from './dialogs/compose/compose.component';
const routes: Routes = [
{
path : 'label/:labelHandle',
component: FuseMailComponent,
resolve : {
mail: MailService
}
},
{
path : 'label/:labelHandle/:mailId',
component: FuseMailComponent,
resolve : {
mail: MailService
}
},
{
path : 'filter/:filterHandle',
component: FuseMailComponent,
resolve : {
mail: MailService
}
},
{
path : 'filter/:filterHandle/:mailId',
component: FuseMailComponent,
resolve : {
mail: MailService
}
},
{
path : ':folderHandle',
component: FuseMailComponent,
resolve : {
mail: MailService
}
},
{
path : ':folderHandle/:mailId',
component: FuseMailComponent,
resolve : {
mail: MailService
}
},
{
path : '**',
redirectTo: 'inbox'
}
];
@NgModule({
declarations : [
FuseMailComponent,
FuseMailListComponent,
FuseMailListItemComponent,
FuseMailDetailsComponent,
FuseMailMainSidenavComponent,
FuseMailComposeDialogComponent
],
imports : [
SharedModule,
RouterModule.forChild(routes)
],
providers : [
MailService
],
entryComponents: [FuseMailComposeDialogComponent]
})
export class FuseMailModule
{
}

View File

@ -1,403 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Http } from '@angular/http';
import { Mail } from './mail.model';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { FuseUtils } from 'app/core/fuseUtils';
@Injectable()
export class MailService implements Resolve<any>
{
mails: Mail[];
selectedMails: Mail[];
currentMail: Mail;
searchText = '';
folders: any[];
filters: any[];
labels: any[];
routeParams: any;
onMailsChanged: BehaviorSubject<any> = new BehaviorSubject([]);
onSelectedMailsChanged: BehaviorSubject<any> = new BehaviorSubject([]);
onCurrentMailChanged: BehaviorSubject<any> = new BehaviorSubject([]);
onFoldersChanged: BehaviorSubject<any> = new BehaviorSubject([]);
onFiltersChanged: BehaviorSubject<any> = new BehaviorSubject([]);
onLabelsChanged: BehaviorSubject<any> = new BehaviorSubject([]);
onSearchTextChanged: BehaviorSubject<any> = new BehaviorSubject('');
constructor(private http: Http)
{
this.selectedMails = [];
}
/**
* Resolve
* @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any}
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
this.routeParams = route.params;
return new Promise((resolve, reject) => {
Promise.all([
this.getFolders(),
this.getFilters(),
this.getLabels(),
this.getMails()
]).then(
() => {
if ( this.routeParams.mailId )
{
this.setCurrentMail(this.routeParams.mailId);
}
else
{
this.setCurrentMail(null);
}
this.onSearchTextChanged.subscribe(searchText => {
if ( searchText !== '' )
{
this.searchText = searchText;
this.getMails();
}
else
{
this.searchText = searchText;
this.getMails();
}
});
resolve();
},
reject
);
});
}
/**
* Get all folders
* @returns {Promise<any>}
*/
getFolders(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/mail-folders')
.subscribe(response => {
this.folders = response.json().data;
this.onFoldersChanged.next(this.folders);
resolve(this.folders);
}, reject);
});
}
/**
* Get all filters
* @returns {Promise<any>}
*/
getFilters(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/mail-filters')
.subscribe(response => {
this.filters = response.json().data;
this.onFiltersChanged.next(this.filters);
resolve(this.filters);
}, reject);
});
}
/**
* Get all labels
* @returns {Promise<any>}
*/
getLabels(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/mail-labels')
.subscribe(response => {
this.labels = response.json().data;
this.onLabelsChanged.next(this.labels);
resolve(this.labels);
}, reject);
});
}
/**
* Get all mails
* @returns {Promise<Mail[]>}
*/
getMails(): Promise<Mail[]>
{
if ( this.routeParams.labelHandle )
{
return this.getMailsByLabel(this.routeParams.labelHandle);
}
if ( this.routeParams.filterHandle )
{
return this.getMailsByFilter(this.routeParams.filterHandle);
}
return this.getMailsByFolder(this.routeParams.folderHandle);
}
/**
* Get mails by folder
* @param handle
* @returns {Promise<Mail[]>}
*/
getMailsByFolder(handle): Promise<Mail[]>
{
return new Promise((resolve, reject) => {
this.http.get('api/mail-folders?handle=' + handle)
.subscribe(folders => {
const folderId = folders.json().data[0].id;
this.http.get('api/mail-mails?folder=' + folderId)
.subscribe(mails => {
this.mails = mails.json().data.map(mail => {
return new Mail(mail);
});
this.mails = FuseUtils.filterArrayByString(this.mails, this.searchText);
this.onMailsChanged.next(this.mails);
resolve(this.mails);
}, reject);
});
});
}
/**
* Get mails by filter
* @param handle
* @returns {Promise<Mail[]>}
*/
getMailsByFilter(handle): Promise<Mail[]>
{
return new Promise((resolve, reject) => {
this.http.get('api/mail-mails?' + handle + '=true')
.subscribe(mails => {
this.mails = mails.json().data.map(mail => {
return new Mail(mail);
});
this.mails = FuseUtils.filterArrayByString(this.mails, this.searchText);
this.onMailsChanged.next(this.mails);
resolve(this.mails);
}, reject);
});
}
/**
* Get mails by label
* @param handle
* @returns {Promise<Mail[]>}
*/
getMailsByLabel(handle): Promise<Mail[]>
{
return new Promise((resolve, reject) => {
this.http.get('api/mail-labels?handle=' + handle)
.subscribe(labels => {
const labelId = labels.json().data[0].id;
this.http.get('api/mail-mails?labels=' + labelId)
.subscribe(mails => {
this.mails = mails.json().data.map(mail => {
return new Mail(mail);
});
this.mails = FuseUtils.filterArrayByString(this.mails, this.searchText);
this.onMailsChanged.next(this.mails);
resolve(this.mails);
}, reject);
});
});
}
/**
* Toggle selected mail by id
* @param id
*/
toggleSelectedMail(id)
{
// First, check if we already have that mail as selected...
if ( this.selectedMails.length > 0 )
{
for ( const mail of this.selectedMails )
{
// ...delete the selected mail
if ( mail.id === id )
{
const index = this.selectedMails.indexOf(mail);
if ( index !== -1 )
{
this.selectedMails.splice(index, 1);
// Trigger the next event
this.onSelectedMailsChanged.next(this.selectedMails);
// Return
return;
}
}
}
}
// If we don't have it, push as selected
this.selectedMails.push(
this.mails.find(mail => {
return mail.id === id;
})
);
// Trigger the next event
this.onSelectedMailsChanged.next(this.selectedMails);
}
/**
* Toggle select all
*/
toggleSelectAll()
{
if ( this.selectedMails.length > 0 )
{
this.deselectMails();
}
else
{
this.selectMails();
}
}
selectMails(filterParameter?, filterValue?)
{
this.selectedMails = [];
// If there is no filter, select all mails
if ( filterParameter === undefined || filterValue === undefined )
{
this.selectedMails = this.mails;
}
else
{
this.selectedMails.push(...
this.mails.filter(mail => {
return mail[filterParameter] === filterValue;
})
);
}
// Trigger the next event
this.onSelectedMailsChanged.next(this.selectedMails);
}
deselectMails()
{
this.selectedMails = [];
// Trigger the next event
this.onSelectedMailsChanged.next(this.selectedMails);
}
/**
* Set current mail by id
* @param id
*/
setCurrentMail(id)
{
this.currentMail = this.mails.find(mail => {
return mail.id === id;
});
this.onCurrentMailChanged.next(this.currentMail);
}
/**
* Toggle label on selected mails
* @param labelId
*/
toggleLabelOnSelectedMails(labelId)
{
this.selectedMails.map(mail => {
const index = mail.labels.indexOf(labelId);
if ( index !== -1 )
{
mail.labels.splice(index, 1);
}
else
{
mail.labels.push(labelId);
}
this.updateMail(mail);
});
}
/**
* Set folder on selected mails
* @param folderId
*/
setFolderOnSelectedMails(folderId)
{
this.selectedMails.map(mail => {
mail.folder = folderId;
this.updateMail(mail);
});
this.deselectMails();
}
/**
* Update the mail
* @param mail
* @returns {Promise<any>}
*/
updateMail(mail)
{
return new Promise((resolve, reject) => {
this.http.post('api/mail-mails/' + mail.id, {...mail})
.subscribe(response => {
this.getMails().then(mails => {
if ( mails && this.currentMail )
{
this.setCurrentMail(this.currentMail.id);
}
resolve(mails);
}, reject);
});
});
}
}

View File

@ -1,67 +0,0 @@
<!-- SIDENAV HEADER -->
<div fxLayout="column" fxLayoutAlign="space-between start"
class="header p-24 md-accent-bg" class.gt-md="header p-24 white-fg">
<div class="logo" fxFlex fxLayout="row" fxLayoutAlign="start center">
<md-icon class="logo-icon s-32">mail</md-icon>
<span class="logo-text">Mailbox</span>
</div>
<div class="account" fxLayout="column">
<div class="title">John Doe</div>
<md-select class="account-selection" placeholder="Mail Selection"
floatPlaceholder="never"
[ngModel]="selectedAccount">
<md-option *ngFor="let account of (accounts | keys)" [value]="account.key">
{{account.value}}
</md-option>
</md-select>
</div>
</div>
<!-- / SIDENAV HEADER -->
<!-- SIDENAV CONTENT -->
<div class="content" perfect-scrollbar>
<div class="p-24">
<button md-raised-button fxFlex
class="mat-accent compose-dialog-button"
(click)="composeDialog($event)"
aria-label="Compose">
COMPOSE
</button>
</div>
<div class="nav">
<div class="nav-subheader">FOLDERS</div>
<div class="nav-item" *ngFor="let folder of folders">
<a class="nav-link" md-ripple [routerLink]="'/apps/mail/' + folder.handle" routerLinkActive="active">
<md-icon class="nav-link-icon" *ngIf="folder.icon">{{folder.icon}}</md-icon>
<span>{{folder.title}}</span>
</a>
</div>
<div class="nav-subheader">FILTERS</div>
<div class="nav-item" *ngFor="let filter of filters">
<a class="nav-link" md-ripple [routerLink]="'/apps/mail/filter/' + filter.handle" routerLinkActive="active">
<md-icon class="nav-link-icon" *ngIf="filter.icon">{{filter.icon}}</md-icon>
<span>{{filter.title}}</span>
</a>
</div>
<div class="nav-subheader">LABELS</div>
<div class="nav-item" *ngFor="let label of labels">
<a class="nav-link" md-ripple [routerLink]="'/apps/mail/label/' + label.handle" routerLinkActive="active">
<md-icon class="nav-link-icon" [ngStyle]="{'color':label.color}">label</md-icon>
<span>{{label.title}}</span>
</a>
</div>
</div>
</div>
<!-- / SIDENAV CONTENT -->

View File

@ -1,30 +0,0 @@
:host {
display: flex;
flex: 1 0 auto;
flex-direction: column;
height: 100%;
.header {
.logo {
.logo-icon {
margin: 0 16px 0 0;
}
.logo-text {
font-size: 24px;
line-height: 24px;
}
}
.account {
width: 100%;
}
}
.content {
.compose-dialog-button {
}
}
}

View File

@ -1,98 +0,0 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MailService } from '../../mail.service';
import { Subscription } from 'rxjs/Subscription';
import { FuseMailComposeDialogComponent } from '../../dialogs/compose/compose.component';
import { MdDialog } from '@angular/material';
import { FormGroup } from '@angular/forms';
@Component({
selector : 'fuse-mail-main-sidenav',
templateUrl: './main-sidenav.component.html',
styleUrls : ['./main-sidenav.component.scss']
})
export class FuseMailMainSidenavComponent implements OnInit, OnDestroy
{
folders: any[];
filters: any[];
labels: any[];
accounts: object;
selectedAccount: string;
dialogRef: any;
onFoldersChanged: Subscription;
onFiltersChanged: Subscription;
onLabelsChanged: Subscription;
constructor(
private mailService: MailService,
public dialog: MdDialog
)
{
// Data
this.accounts = {
'creapond' : 'johndoe@creapond.com',
'withinpixels': 'johndoe@withinpixels.com'
};
this.selectedAccount = 'creapond';
}
ngOnInit()
{
this.onFoldersChanged =
this.mailService.onFoldersChanged
.subscribe(folders => {
this.folders = folders;
});
this.onFiltersChanged =
this.mailService.onFiltersChanged
.subscribe(filters => {
this.filters = filters;
});
this.onLabelsChanged =
this.mailService.onLabelsChanged
.subscribe(labels => {
this.labels = labels;
});
}
composeDialog()
{
this.dialogRef = this.dialog.open(FuseMailComposeDialogComponent, {
panelClass: 'mail-compose-dialog'
});
this.dialogRef.afterClosed()
.subscribe(response => {
if ( !response )
{
return;
}
const actionType: string = response[0];
const formData: FormGroup = response[1];
switch ( actionType )
{
/**
* Send
*/
case 'send':
console.log('new Mail', formData.getRawValue());
break;
/**
* Delete
*/
case 'delete':
console.log('delete Mail');
break;
}
});
}
ngOnDestroy()
{
this.onFoldersChanged.unsubscribe();
this.onFiltersChanged.unsubscribe();
this.onLabelsChanged.unsubscribe();
}
}

View File

@ -1,65 +0,0 @@
<!-- SIDENAV HEADER -->
<div fxLayout="column" fxLayoutAlign="space-between start"
class="header p-24 md-accent-bg" class.gt-md="header p-24 white-fg">
<div class="logo" fxFlex fxLayout="row" fxLayoutAlign="start center">
<md-icon class="logo-icon s-32">check_box</md-icon>
<span class="logo-text">To-do</span>
</div>
<div class="account" fxLayout="column">
<div class="title">John Doe</div>
<md-select class="account-selection" placeholder="Todo Selection"
floatPlaceholder="never"
[ngModel]="selectedAccount">
<md-option *ngFor="let account of (accounts | keys)" [value]="account.key">
{{account.value}}
</md-option>
</md-select>
</div>
</div>
<!-- / SIDENAV HEADER -->
<!-- SIDENAV CONTENT -->
<div class="content" perfect-scrollbar>
<div class="p-24">
<button md-raised-button fxFlex
class="mat-accent add-todo-button"
(click)="newTodo()"
aria-label="ADD TASK">
ADD TASK
</button>
</div>
<div class="nav">
<div class="nav-item">
<a class="nav-link" md-ripple [routerLink]="'/apps/todo/all'" routerLinkActive="active">
<md-icon class="nav-link-icon">view_headline</md-icon>
<span>All</span>
</a>
</div>
<div class="nav-subheader">FILTERS</div>
<div class="nav-item" *ngFor="let filter of filters">
<a class="nav-link" md-ripple [routerLink]="'/apps/todo/filter/' + filter.handle" routerLinkActive="active">
<md-icon class="nav-link-icon" *ngIf="filter.icon">{{filter.icon}}</md-icon>
<span>{{filter.title}}</span>
</a>
</div>
<div class="nav-subheader">TAGS</div>
<div class="nav-item" *ngFor="let tag of tags">
<a class="nav-link" md-ripple [routerLink]="'/apps/todo/tag/' + tag.handle" routerLinkActive="active">
<md-icon class="nav-link-icon" [ngStyle]="{'color':tag.color}">label</md-icon>
<span>{{tag.title}}</span>
</a>
</div>
</div>
</div>
<!-- / SIDENAV CONTENT -->

View File

@ -1,33 +0,0 @@
:host {
display: flex;
flex: 1 0 auto;
flex-direction: column;
height: 100%;
.header {
.logo {
.logo-icon {
margin: 0 16px 0 0;
}
.logo-text {
font-size: 24px;
line-height: 24px;
}
}
.account {
width: 100%;
.account-selection {
}
}
}
.content {
.add-todo-button {
}
}
}

View File

@ -1,62 +0,0 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { TodoService } from '../../todo.service';
import { Subscription } from 'rxjs/Subscription';
import { Router } from '@angular/router';
@Component({
selector : 'fuse-todo-main-sidenav',
templateUrl: './main-sidenav.component.html',
styleUrls : ['./main-sidenav.component.scss']
})
export class FuseTodoMainSidenavComponent implements OnInit, OnDestroy
{
folders: any[];
filters: any[];
tags: any[];
accounts: object;
selectedAccount: string;
onFiltersChanged: Subscription;
onTagsChanged: Subscription;
constructor(private todoService: TodoService, private router: Router)
{
// Data
this.accounts = {
'creapond' : 'johndoe@creapond.com',
'withinpixels': 'johndoe@withinpixels.com'
};
this.selectedAccount = 'creapond';
}
ngOnInit()
{
this.onFiltersChanged =
this.todoService.onFiltersChanged
.subscribe(filters => {
this.filters = filters;
});
this.onTagsChanged =
this.todoService.onTagsChanged
.subscribe(tags => {
this.tags = tags;
});
}
ngOnDestroy()
{
this.onFiltersChanged.unsubscribe();
this.onTagsChanged.unsubscribe();
}
newTodo()
{
this.router.navigate(['/apps/todo/all']).then(() => {
setTimeout(() => {
this.todoService.onNewTodoClicked.next('');
});
});
}
}

View File

@ -1,112 +0,0 @@
<div *ngIf="!todo" fxLayout="column" fxLayoutAlign="center center" fxFlex>
<md-icon class="s-120 mb-12">check_box</md-icon>
<span class="hint-text mat-h1">Select a todo</span>
</div>
<div *ngIf="todo">
<div class="todo-header" fxLayout="row" fxLayoutAlign="space-between center">
<button md-button class="mat-icon-button toggle-complete-button" (click)="toggleCompleted($event)"
aria-label="Toggle completed" fxFlex="0 1 auto">
<md-icon *ngIf="todo.completed">check_box</md-icon>
<md-icon *ngIf="!todo.completed">check_box_outline_blank</md-icon>
</button>
<div class="actions" fxLayout="row" fxLayoutAlign="start center">
<button md-button class="mat-icon-button" (click)="toggleDeleted($event)" aria-label="Toggle delete">
<md-icon *ngIf="todo.deleted">delete_forever</md-icon>
<md-icon *ngIf="!todo.deleted">delete</md-icon>
</button>
<button md-button class="mat-icon-button" (click)="toggleImportant($event)" aria-label="Toggle important">
<md-icon *ngIf="todo.important">error</md-icon>
<md-icon *ngIf="!todo.important">error_outline</md-icon>
</button>
<button md-button class="mat-icon-button" (click)="toggleStar($event)" aria-label="Toggle star">
<md-icon *ngIf="todo.starred">star</md-icon>
<md-icon *ngIf="!todo.starred">star_outline</md-icon>
</button>
<button md-icon-button [mdMenuTriggerFor]="labelMenu" fxFlex="0 1 auto">
<md-icon>label</md-icon>
</button>
<md-menu #labelMenu="mdMenu">
<button md-menu-item *ngFor="let tag of tags"
(click)="toggleTagOnTodo(tag.id)">
{{tag.title}}
</button>
</md-menu>
</div>
</div>
<div class="todo-content">
<form [formGroup]="todoForm" (submit)="addTodo()">
<md-input-container class="title mt-8" floatPlaceholder="never" fxFill>
<textarea mdInput
#titleInput
name="title"
formControlName="title"
placeholder="Title"
mdTextareaAutosize
required>
</textarea>
</md-input-container>
<div class="tags mb-24" fxFlexFill fxLayout="row" fxLayoutWrap>
<div class="tag" fxLayout="row" fxLayoutAlign="start center" *ngFor="let tagId of todo.tags">
<div class="tag-color" [ngStyle]="{'background-color': tags | getById:tagId:'color'}"></div>
<div class="tag-label">{{tags | getById:tagId:'title'}}</div>
</div>
</div>
<div fxFlexFill fxLayout="row">
<md-input-container fxFlex class="mr-16">
<input mdInput
name="start"
formControlName="startDate"
[mdDatepicker]="startDatePicker"
placeholder="Start Date">
<button mdSuffix [mdDatepickerToggle]="startDatePicker"></button>
</md-input-container>
<md-datepicker #startDatePicker></md-datepicker>
<md-input-container fxFlex>
<input mdInput
name="dueDate"
formControlName="dueDate"
[mdDatepicker]="dueDatePicker"
placeholder="Due Date">
<button mdSuffix [mdDatepickerToggle]="dueDatePicker"></button>
</md-input-container>
<md-datepicker #dueDatePicker></md-datepicker>
</div>
<md-input-container class="" fxFill>
<textarea mdInput
name="notes"
formControlName="notes"
placeholder="Notes"
md-maxlength="500"
mdTextareaAutosize
mdAutosizeMinRows="6">
</textarea>
</md-input-container>
<button *ngIf="formType === 'new'"
md-raised-button class="mat-accent"
[disabled]="todoForm.invalid">SAVE
</button>
</form>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More