mirror of
https://github.com/richard-loafle/fuse-angular.git
synced 2025-01-10 04:25:08 +00:00
skeleton branch
This commit is contained in:
parent
8cbc2f3ab7
commit
d7003711ee
|
@ -10,44 +10,17 @@ import 'hammerjs';
|
|||
|
||||
import { SharedModule } from './core/modules/shared.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { ProjectModule } from './main/content/apps/dashboards/project/project.module';
|
||||
import { PerfectScrollbarConfigInterface, PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||
import { FuseFakeDbService } from './fuse-fake-db/fuse-fake-db.service';
|
||||
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 { FuseConfigService } from './core/services/config.service';
|
||||
import { FuseSampleModule } from './main/content/sample/sample.module';
|
||||
|
||||
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 : '**',
|
||||
redirectTo: 'apps/dashboards/project'
|
||||
redirectTo: 'sample'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -62,18 +35,10 @@ const appRoutes: Routes = [
|
|||
BrowserAnimationsModule,
|
||||
RouterModule.forRoot(appRoutes),
|
||||
SharedModule,
|
||||
|
||||
InMemoryWebApiModule.forRoot(FuseFakeDbService, {delay: 125}),
|
||||
|
||||
PerfectScrollbarModule.forRoot(),
|
||||
|
||||
FuseMainModule,
|
||||
|
||||
ProjectModule,
|
||||
|
||||
PagesModule,
|
||||
UIModule,
|
||||
ComponentsModule
|
||||
FuseSampleModule
|
||||
],
|
||||
providers : [
|
||||
FuseSplashScreenService,
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { EventEmitter, Injectable } from '@angular/core';
|
||||
import { FuseNavigation } from './navigation.model';
|
||||
import { FuseNavigation } from '../../../navigation.model';
|
||||
|
||||
@Injectable()
|
||||
export class FuseNavigationService
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -1,12 +0,0 @@
|
|||
.event-form-dialog {
|
||||
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
width: 720px;
|
||||
}
|
||||
}
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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 || ''
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
|
@ -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 -->
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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 -->
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
<div [ngSwitch]="view" class="views">
|
||||
|
||||
<fuse-chat-contact-sidenav class="view"
|
||||
*ngSwitchCase="'contact'"
|
||||
[@slideInRight]>
|
||||
</fuse-chat-contact-sidenav>
|
||||
|
||||
</div>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -1,16 +0,0 @@
|
|||
.contact-form-dialog {
|
||||
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
width: 400px;
|
||||
|
||||
.toolbar-bottom {
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
|
@ -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]
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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 -->
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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 || '';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -1,8 +0,0 @@
|
|||
@import "src/app/core/scss/fuse";
|
||||
|
||||
#contacts {
|
||||
|
||||
.content {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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 -->
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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">°</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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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('>');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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 -->
|
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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 -->
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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('')
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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 -->
|
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 -->
|
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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('');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue
Block a user