mirror of
https://github.com/richard-loafle/fuse-angular.git
synced 2025-12-23 19:27:06 +00:00
Compare commits
25 Commits
v1.2.3-ske
...
v1.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e477f797d0 | ||
|
|
fb196c3864 | ||
|
|
5cf44962fc | ||
|
|
06b0c3775a | ||
|
|
26690990f0 | ||
|
|
377092d9ec | ||
|
|
abede386c8 | ||
|
|
242feaa169 | ||
|
|
7c2494a82c | ||
|
|
5c2e717a40 | ||
|
|
6ae0a9760d | ||
|
|
2a10f3e443 | ||
|
|
db7a00440c | ||
|
|
0e1c589399 | ||
|
|
2f419b1af5 | ||
|
|
effd3cefcb | ||
|
|
21fd488a8e | ||
|
|
37a5c69269 | ||
|
|
9f440b1bf2 | ||
|
|
a65f61cce4 | ||
|
|
0d8fe0be72 | ||
|
|
2bbc90af64 | ||
|
|
ad21d9fed5 | ||
|
|
4c6ef29e20 | ||
|
|
a74c5108fd |
550
package-lock.json
generated
550
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
63
package.json
63
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "fuse2",
|
||||
"version": "1.2.3",
|
||||
"license": "",
|
||||
"version": "1.3.0",
|
||||
"license": "https://themeforest.net/licenses/terms/regular",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
@@ -16,56 +16,61 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@agm/core": "1.0.0-beta.2",
|
||||
"@angular/animations": "5.0.3",
|
||||
"@angular/cdk": "5.0.0-rc.1",
|
||||
"@angular/common": "5.0.3",
|
||||
"@angular/compiler": "5.0.3",
|
||||
"@angular/core": "5.0.3",
|
||||
"@angular/animations": "5.1.1",
|
||||
"@angular/cdk": "5.0.1",
|
||||
"@angular/common": "5.1.1",
|
||||
"@angular/compiler": "5.1.1",
|
||||
"@angular/core": "5.1.1",
|
||||
"@angular/flex-layout": "2.0.0-beta.10-4905443",
|
||||
"@angular/forms": "5.0.3",
|
||||
"@angular/http": "5.0.3",
|
||||
"@angular/material": "5.0.0-rc.1",
|
||||
"@angular/material-moment-adapter": "5.0.0-rc.1",
|
||||
"@angular/platform-browser": "5.0.3",
|
||||
"@angular/platform-browser-dynamic": "5.0.3",
|
||||
"@angular/router": "5.0.3",
|
||||
"@ngx-translate/core": "8.0.0",
|
||||
"@angular/forms": "5.1.1",
|
||||
"@angular/http": "5.1.1",
|
||||
"@angular/material": "5.0.1",
|
||||
"@angular/material-moment-adapter": "5.0.1",
|
||||
"@angular/platform-browser": "5.1.1",
|
||||
"@angular/platform-browser-dynamic": "5.1.1",
|
||||
"@angular/router": "5.1.1",
|
||||
"@ngx-translate/core": "9.0.1",
|
||||
"@swimlane/ngx-charts": "7.0.1",
|
||||
"@swimlane/ngx-datatable": "11.1.4",
|
||||
"@withinpixels/ngx-dnd": "3.1.0",
|
||||
"@swimlane/ngx-datatable": "11.1.5",
|
||||
"@swimlane/ngx-dnd": "3.1.0",
|
||||
"angular-calendar": "0.22.1",
|
||||
"angular-in-memory-web-api": "0.5.1",
|
||||
"angular-in-memory-web-api": "0.5.2",
|
||||
"angular2-markdown": "1.6.0",
|
||||
"classlist.js": "1.1.20150312",
|
||||
"core-js": "2.5.1",
|
||||
"core-js": "2.5.3",
|
||||
"d3": "4.12.0",
|
||||
"hammerjs": "2.0.8",
|
||||
"highlight.js": "9.12.0",
|
||||
"intl": "1.2.5",
|
||||
"moment": "2.19.2",
|
||||
"ngx-color-picker": "5.0.4",
|
||||
"moment": "2.19.3",
|
||||
"@ngrx/effects": "4.1.1",
|
||||
"@ngrx/router-store": "4.1.1",
|
||||
"@ngrx/store": "4.1.1",
|
||||
"@ngrx/store-devtools": "4.1.1",
|
||||
"ngrx-store-freeze": "0.2.0",
|
||||
"ngx-color-picker": "5.1.0",
|
||||
"ngx-cookie-service": "1.0.9",
|
||||
"perfect-scrollbar": "1.2.0",
|
||||
"rxjs": "5.5.2",
|
||||
"perfect-scrollbar": "1.3.0",
|
||||
"rxjs": "5.5.5",
|
||||
"web-animations-js": "2.3.1",
|
||||
"zone.js": "0.8.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "1.5.4",
|
||||
"@angular/compiler-cli": "5.0.3",
|
||||
"@angular/language-service": "5.0.3",
|
||||
"@angular/cli": "1.6.1",
|
||||
"@angular/compiler-cli": "5.1.1",
|
||||
"@angular/language-service": "5.1.1",
|
||||
"@angularclass/hmr": "2.1.3",
|
||||
"@types/jasmine": "2.5.54",
|
||||
"@types/jasminewd2": "2.0.3",
|
||||
"@types/node": "6.0.92",
|
||||
"codelyzer": "4.0.1",
|
||||
"@types/node": "6.0.94",
|
||||
"codelyzer": "4.0.2",
|
||||
"jasmine-core": "2.6.4",
|
||||
"jasmine-spec-reporter": "4.1.1",
|
||||
"karma": "1.7.1",
|
||||
"karma-chrome-launcher": "2.1.1",
|
||||
"karma-cli": "1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "1.3.0",
|
||||
"karma-jasmine": "1.1.0",
|
||||
"karma-jasmine": "1.1.1",
|
||||
"karma-jasmine-html-reporter": "0.2.2",
|
||||
"protractor": "5.1.2",
|
||||
"ts-node": "3.2.2",
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FuseSplashScreenService } from './core/services/splash-screen.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { FuseTranslationLoaderService } from './core/services/translation-loader.service';
|
||||
|
||||
import { FuseNavigationService } from './core/components/navigation/navigation.service';
|
||||
import { FuseNavigationModel } from './navigation/navigation.model';
|
||||
import { locale as navigationEnglish } from './navigation/i18n/en';
|
||||
import { locale as navigationTurkish } from './navigation/i18n/tr';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-root',
|
||||
@@ -10,8 +16,10 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
export class AppComponent
|
||||
{
|
||||
constructor(
|
||||
private fuseNavigationService: FuseNavigationService,
|
||||
private fuseSplashScreen: FuseSplashScreenService,
|
||||
private translate: TranslateService
|
||||
private translate: TranslateService,
|
||||
private translationLoader: FuseTranslationLoaderService
|
||||
)
|
||||
{
|
||||
// Add languages
|
||||
@@ -22,5 +30,11 @@ export class AppComponent
|
||||
|
||||
// Use a language
|
||||
this.translate.use('en');
|
||||
|
||||
// Set the navigation model
|
||||
this.fuseNavigationService.setNavigationModel(new FuseNavigationModel());
|
||||
|
||||
// Set the navigation translations
|
||||
this.translationLoader.loadTranslations(navigationEnglish, navigationTurkish);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
@@ -22,12 +21,17 @@ import { ServicesModule } from './main/content/services/services.module';
|
||||
import { FuseAngularMaterialModule } from './main/content/components/angular-material/angular-material.module';
|
||||
import { MarkdownModule } from 'angular2-markdown';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { AppStoreModule } from './store/store.module';
|
||||
|
||||
const appRoutes: Routes = [
|
||||
{
|
||||
path : 'apps/mail',
|
||||
loadChildren: './main/content/apps/mail/mail.module#FuseMailModule'
|
||||
},
|
||||
{
|
||||
path : 'apps/mail-ngrx',
|
||||
loadChildren: './main/content/apps/mail-ngrx/mail.module#FuseMailNgrxModule'
|
||||
},
|
||||
{
|
||||
path : 'apps/chat',
|
||||
loadChildren: './main/content/apps/chat/chat.module#FuseChatModule'
|
||||
@@ -68,23 +72,19 @@ const appRoutes: Routes = [
|
||||
],
|
||||
imports : [
|
||||
BrowserModule,
|
||||
HttpModule,
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
RouterModule.forRoot(appRoutes),
|
||||
SharedModule,
|
||||
MarkdownModule.forRoot(),
|
||||
TranslateModule.forRoot(),
|
||||
|
||||
InMemoryWebApiModule.forRoot(FuseFakeDbService, {
|
||||
delay : 0,
|
||||
passThruUnknownUrl: true
|
||||
}),
|
||||
|
||||
AppStoreModule,
|
||||
FuseMainModule,
|
||||
|
||||
ProjectModule,
|
||||
|
||||
PagesModule,
|
||||
UIModule,
|
||||
ServicesModule,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<a class="nav-link" matRipple>
|
||||
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
|
||||
<span class="nav-link-title">{{item.title}}</span>
|
||||
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
|
||||
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
|
||||
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
|
||||
{{item.badge.title}}
|
||||
</span>
|
||||
<mat-icon class="collapse-arrow">keyboard_arrow_right</mat-icon>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<a class="nav-link" *ngIf="item.url" [routerLink]="[item.url]" routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
|
||||
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
|
||||
<span class="nav-link-title">{{item.title}}</span>
|
||||
<span class="nav-link-badge" *ngIf="item.badge"
|
||||
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
|
||||
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
|
||||
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
|
||||
{{item.badge.title}}
|
||||
</span>
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
<span class="nav-link" *ngIf="item.function" (click)="item.function()" matRipple>
|
||||
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
|
||||
<span class="nav-link-title">{{item.title}}</span>
|
||||
<span class="nav-link-badge" *ngIf="item.badge"
|
||||
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
|
||||
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
|
||||
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
|
||||
{{item.badge.title}}
|
||||
</span>
|
||||
|
||||
5
src/app/core/components/navigation/navigation.model.ts
Normal file
5
src/app/core/components/navigation/navigation.model.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface FuseNavigationModelInterface
|
||||
{
|
||||
model: any[];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EventEmitter, Injectable } from '@angular/core';
|
||||
import { NavigationModel } from '../../../navigation.model';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { FuseNavigationModelInterface } from './navigation.model';
|
||||
|
||||
@Injectable()
|
||||
export class FuseNavigationService
|
||||
@@ -8,13 +8,11 @@ export class FuseNavigationService
|
||||
onNavCollapseToggle = new EventEmitter<any>();
|
||||
onNavCollapseToggled = new EventEmitter<any>();
|
||||
onNavigationModelChange: BehaviorSubject<any> = new BehaviorSubject({});
|
||||
navigationModel: NavigationModel;
|
||||
navigationModel: FuseNavigationModelInterface;
|
||||
flatNavigation: any[] = [];
|
||||
|
||||
constructor()
|
||||
{
|
||||
this.navigationModel = new NavigationModel();
|
||||
this.onNavigationModelChange.next(this.navigationModel.model);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,8 +153,15 @@ export class FuseNavigationService
|
||||
*/
|
||||
getFlatNavigation(navigationItems?)
|
||||
{
|
||||
// If navigation items not provided,
|
||||
// that means we are running the function
|
||||
// for the first time...
|
||||
if ( !navigationItems )
|
||||
{
|
||||
// Reset the flat navigation
|
||||
this.flatNavigation = [];
|
||||
|
||||
// Get the entire navigation model
|
||||
navigationItems = this.navigationModel.model;
|
||||
}
|
||||
|
||||
@@ -181,7 +186,10 @@ export class FuseNavigationService
|
||||
|
||||
if ( navItem.type === 'collapse' || navItem.type === 'group' )
|
||||
{
|
||||
this.getFlatNavigation(navItem.children);
|
||||
if ( navItem.children )
|
||||
{
|
||||
this.getFlatNavigation(navItem.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<a class="nav-link" matRipple (click)="toggleOpen($event)">
|
||||
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
|
||||
<span class="nav-link-title">{{item.title}}</span>
|
||||
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
|
||||
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
|
||||
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
|
||||
{{item.badge.title}}
|
||||
</span>
|
||||
<mat-icon class="collapse-arrow">keyboard_arrow_right</mat-icon>
|
||||
</a>
|
||||
|
||||
<div class="children" [@slideInOut]="isOpen">
|
||||
<ng-container *ngFor="let item of item.children">
|
||||
<fuse-nav-vertical-item *ngIf="item.type=='item'" [item]="item"></fuse-nav-vertical-item>
|
||||
|
||||
@@ -69,6 +69,20 @@ export class FuseNavVerticalCollapseComponent implements OnInit
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
// Check if the url can be found in
|
||||
// one of the children of this item
|
||||
if ( this.isUrlInChildren(this.item, this.router.url) )
|
||||
{
|
||||
this.expand();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.collapse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle collapse
|
||||
*
|
||||
@@ -108,6 +122,7 @@ export class FuseNavVerticalCollapseComponent implements OnInit
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = false;
|
||||
this.navigationService.onNavCollapseToggle.emit();
|
||||
}
|
||||
@@ -175,8 +190,4 @@ export class FuseNavVerticalCollapseComponent implements OnInit
|
||||
return false;
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div class="group-title">
|
||||
<span class="hint-text">{{ item.title }}</span>
|
||||
<span class="hint-text" [translate]="item.translate">{{ item.title }}</span>
|
||||
</div>
|
||||
|
||||
<div class="group-items">
|
||||
<ng-container *ngFor="let item of item.children">
|
||||
<fuse-nav-vertical-group *ngIf="item.type=='group'" [item]="item"></fuse-nav-vertical-group>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<a class="nav-link" *ngIf="item.url" [routerLink]="[item.url]" routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
|
||||
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
|
||||
<span class="nav-link-title">{{item.title}}</span>
|
||||
<span class="nav-link-badge" *ngIf="item.badge"
|
||||
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
|
||||
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
|
||||
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
|
||||
{{item.badge.title}}
|
||||
</span>
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
<span class="nav-link" *ngIf="item.function" (click)="item.function()" matRipple>
|
||||
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
|
||||
<span class="nav-link-title">{{item.title}}</span>
|
||||
<span class="nav-link-badge" *ngIf="item.badge"
|
||||
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
|
||||
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
|
||||
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
|
||||
{{item.badge.title}}
|
||||
</span>
|
||||
|
||||
@@ -62,4 +62,23 @@ fuse-widget {
|
||||
transform: rotateY(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.mat-form-field {
|
||||
|
||||
&.mat-form-field-type-mat-select {
|
||||
|
||||
.mat-input-wrapper {
|
||||
padding: 16px 0;
|
||||
|
||||
.mat-input-infix {
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-input-underline {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ export class FuseWidgetComponent implements OnInit, AfterContentInit
|
||||
setTimeout(() => {
|
||||
|
||||
this.toggleButtons.forEach(flipButton => {
|
||||
this.renderer.listen(flipButton.el.nativeElement, 'click', () => {
|
||||
this.renderer.listen(flipButton.el.nativeElement, 'click', (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.toggle();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ export class FusePerfectScrollbarDirective implements OnInit, AfterViewInit, OnD
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
if ( !this.isInitialized )
|
||||
if ( !this.isInitialized || !this.ps )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { MaterialModule } from './material.module';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { ColorPickerModule } from 'ngx-color-picker';
|
||||
import { NgxDnDModule } from '@withinpixels/ngx-dnd';
|
||||
import { NgxDnDModule } from '@swimlane/ngx-dnd';
|
||||
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
|
||||
|
||||
import { FuseMatSidenavHelperDirective, FuseMatSidenavTogglerDirective } from '../directives/fuse-mat-sidenav-helper/fuse-mat-sidenav-helper.directive';
|
||||
|
||||
@@ -28,3 +28,51 @@
|
||||
.mat-form-field-underline {
|
||||
background-color: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
// Fix: "Some idiots using table-cell and inline-table in mat-select"
|
||||
.mat-form-field {
|
||||
|
||||
&.mat-form-field-type-mat-select {
|
||||
|
||||
.mat-input-infix {
|
||||
display: inline-flex;
|
||||
width: auto;
|
||||
|
||||
.mat-select-trigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.mat-select-value {
|
||||
display: flex;
|
||||
max-width: none;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.mat-select-arrow-wrapper {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix: "Stepper icons are broken due to Fuse's icon helpers"
|
||||
mat-horizontal-stepper,
|
||||
mat-vertical-stepper {
|
||||
|
||||
mat-step-header {
|
||||
|
||||
mat-icon {
|
||||
height: 16px !important;
|
||||
width: 16px !important;
|
||||
min-width: 0 !important;
|
||||
min-height: 0 !important;
|
||||
color: rgba(255, 255, 255, 0.87) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mat-vertical-stepper {
|
||||
padding: 16px 0;
|
||||
}
|
||||
@@ -47,14 +47,20 @@
|
||||
}
|
||||
|
||||
.nav-link-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
padding: 0 7px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
border-radius: 20px;
|
||||
transition: opacity 0.2s ease-in-out 0.1s;
|
||||
margin-left: 8px;
|
||||
|
||||
+ .collapse-arrow {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
fuse-footer,
|
||||
fuse-quick-panel,
|
||||
fuse-theme-options,
|
||||
.ps > .ps__scrollbar-x-rail,
|
||||
.ps > .ps__scrollbar-y-rail {
|
||||
.ps > .ps__rail-x,
|
||||
.ps > .ps__rail-y {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,20 +3,21 @@
|
||||
.color-picker {
|
||||
height: auto !important;
|
||||
border: none !important;
|
||||
|
||||
@include mat-elevation(4);
|
||||
|
||||
.preset-area {
|
||||
//padding: 4px 15px;
|
||||
padding: 0 0 12px 12px !important;
|
||||
padding: 0 0 16px 16px !important;
|
||||
height: 140px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
> hr {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preset-label {
|
||||
display: none;
|
||||
|
||||
}
|
||||
|
||||
.preset-color {
|
||||
@@ -25,7 +26,8 @@
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
border-radius: 0 !important;
|
||||
&:nth-child(14n+3) {
|
||||
|
||||
&:nth-child(14n+1) {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,52 +216,52 @@
|
||||
}
|
||||
|
||||
&.Jan {
|
||||
background-image: url('/assets/images/backgrounds/january.jpg');
|
||||
background-position: 0 45%;
|
||||
background-image: url('/assets/images/backgrounds/winter.jpg');
|
||||
background-position: 0 65%;
|
||||
}
|
||||
&.Feb {
|
||||
background-image: url('/assets/images/backgrounds/february.jpg');
|
||||
background-position: 0 50%;
|
||||
background-image: url('/assets/images/backgrounds/winter.jpg');
|
||||
background-position: 0 65%;
|
||||
}
|
||||
&.Mar {
|
||||
background-image: url('/assets/images/backgrounds/march.jpg');
|
||||
background-position: 0 45%;
|
||||
background-image: url('/assets/images/backgrounds/spring.jpg');
|
||||
background-position: 0 19%;
|
||||
}
|
||||
&.Apr {
|
||||
background-image: url('/assets/images/backgrounds/april.jpg');
|
||||
background-position: 0 48%;
|
||||
background-image: url('/assets/images/backgrounds/spring.jpg');
|
||||
background-position: 0 19%;
|
||||
}
|
||||
&.May {
|
||||
background-image: url('/assets/images/backgrounds/may.jpg');
|
||||
background-position: 0 47%;
|
||||
background-image: url('/assets/images/backgrounds/spring.jpg');
|
||||
background-position: 0 19%;
|
||||
}
|
||||
&.Jun {
|
||||
background-image: url('/assets/images/backgrounds/june.jpg');
|
||||
background-position: 0 48%;
|
||||
background-image: url('/assets/images/backgrounds/summer.jpg');
|
||||
background-position: 0 91%;
|
||||
}
|
||||
&.Jul {
|
||||
background-image: url('/assets/images/backgrounds/july.jpg');
|
||||
background-position: 0 3%;
|
||||
background-image: url('/assets/images/backgrounds/summer.jpg');
|
||||
background-position: 0 91%;
|
||||
}
|
||||
&.Aug {
|
||||
background-image: url('/assets/images/backgrounds/august.jpg');
|
||||
background-position: 0 61%;
|
||||
background-image: url('/assets/images/backgrounds/summer.jpg');
|
||||
background-position: 0 91%;
|
||||
}
|
||||
&.Sep {
|
||||
background-image: url('/assets/images/backgrounds/september.jpg');
|
||||
background-position: 0 58%;
|
||||
background-image: url('/assets/images/backgrounds/autumn.jpg');
|
||||
background-position: 0 40%;
|
||||
}
|
||||
&.Oct {
|
||||
background-image: url('/assets/images/backgrounds/october.jpg');
|
||||
background-position: 0 50%;
|
||||
background-image: url('/assets/images/backgrounds/autumn.jpg');
|
||||
background-position: 0 40%;
|
||||
}
|
||||
&.Nov {
|
||||
background-image: url('/assets/images/backgrounds/november.jpg');
|
||||
background-position: 0 46%;
|
||||
background-image: url('/assets/images/backgrounds/autumn.jpg');
|
||||
background-position: 0 40%;
|
||||
}
|
||||
&.Dec {
|
||||
background-image: url('/assets/images/backgrounds/december.jpg');
|
||||
background-position: 0 43%;
|
||||
background-image: url('/assets/images/backgrounds/winter.jpg');
|
||||
background-position: 0 65%;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
@@ -299,7 +299,7 @@
|
||||
.add-event-button {
|
||||
position: absolute;
|
||||
right: 18px;
|
||||
bottom: -32px;
|
||||
bottom: -26px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,11 @@
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="py-16" fxFlex="1 0 auto" fxLayout="row" formGroupName="color">
|
||||
<mat-form-field class="mr-24" fxFlex>
|
||||
<div class="py-16" fxFlex="1 0 auto" fxLayout="column" fxLayout.gt-xs="row" formGroupName="color">
|
||||
|
||||
<mat-form-field class="mr-sm-24" fxFlex>
|
||||
<input matInput
|
||||
class="primary-color-input"
|
||||
name="primary color"
|
||||
formControlName="primary"
|
||||
placeholder="Primary color"
|
||||
@@ -44,8 +46,10 @@
|
||||
[style.background]="event.color.primary"
|
||||
(colorPickerChange)="event.color.primary = $event; eventForm.patchValue({color:{primary:$event}})"/>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput
|
||||
class="secondary-color-input"
|
||||
name="secondary color"
|
||||
formControlName="secondary"
|
||||
placeholder="Secondary color"
|
||||
@@ -55,11 +59,12 @@
|
||||
[style.background]="event.color.secondary"
|
||||
(colorPickerChange)="event.color.secondary = $event; eventForm.patchValue({color:{secondary:$event}})"/>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
|
||||
<div fxFlex="1 0 auto" fxLayout="row">
|
||||
<div fxFlex="1 0 auto" fxLayout="column" fxLayout.gt-xs="row">
|
||||
|
||||
<mat-form-field class="mr-24" fxFlex>
|
||||
<mat-form-field class="mr-sm-24" fxFlex>
|
||||
<input matInput [matDatepicker]="startDatePicker" placeholder="Start Date"
|
||||
name="start"
|
||||
formControlName="start">
|
||||
@@ -73,9 +78,9 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div fxFlex="1 0 auto" fxLayout="row">
|
||||
<div fxFlex="1 0 auto" fxLayout="column" fxLayout.gt-xs="row">
|
||||
|
||||
<mat-form-field class="mr-24" fxFlex>
|
||||
<mat-form-field class="mr-sm-24" fxFlex>
|
||||
<input matInput [matDatepicker]="endDatePicker" placeholder="End Date"
|
||||
name="end"
|
||||
formControlName="end">
|
||||
@@ -98,12 +103,12 @@
|
||||
|
||||
<mat-form-field formGroupName="meta" class="w-100-p">
|
||||
|
||||
<textarea matInput
|
||||
formControlName="notes"
|
||||
placeholder="Notes"
|
||||
mat-maxlength="250"
|
||||
max-rows="4">
|
||||
</textarea>
|
||||
<textarea matInput
|
||||
formControlName="notes"
|
||||
placeholder="Notes"
|
||||
mat-maxlength="250"
|
||||
max-rows="4">
|
||||
</textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
@import "src/app/core/scss/fuse";
|
||||
|
||||
.event-form-dialog {
|
||||
|
||||
@include media-breakpoint('xs') {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up('xs') {
|
||||
width: 480px;
|
||||
}
|
||||
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
width: 480px;
|
||||
}
|
||||
|
||||
.dialog-content-wrapper {
|
||||
@@ -10,4 +19,9 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.primary-color-input,
|
||||
.secondary-color-input {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,18 @@
|
||||
|
||||
.contact-form-dialog {
|
||||
|
||||
@include media-breakpoint('xs') {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up('xs') {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
width: 400px;
|
||||
overflow: hidden;
|
||||
|
||||
@include media-breakpoint('xs') {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.mat-toolbar {
|
||||
min-height: initial;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Subject } from 'rxjs/Subject';
|
||||
@Injectable()
|
||||
export class ContactsService implements Resolve<any>
|
||||
{
|
||||
onContactsChanged: BehaviorSubject<any> = new BehaviorSubject({});
|
||||
onContactsChanged: BehaviorSubject<any> = new BehaviorSubject([]);
|
||||
onSelectedContactsChanged: BehaviorSubject<any> = new BehaviorSubject([]);
|
||||
onUserDataChanged: BehaviorSubject<any> = new BehaviorSubject([]);
|
||||
onSearchTextChanged: Subject<any> = new Subject();
|
||||
|
||||
@@ -703,7 +703,7 @@
|
||||
|
||||
<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 class="h3">{{dateNow | date: 'EEEE, HH:mm:ss'}}</div>
|
||||
|
||||
<div>
|
||||
<button mat-icon-button [matMenuTriggerFor]="moreMenu" aria-label="more">
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { EcommerceDashboardService } from './dashboard.service';
|
||||
import * as shape from 'd3-shape';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { DataSource } from '@angular/cdk/collections';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
|
||||
@Component({
|
||||
@@ -23,14 +19,10 @@ export class FuseEcommerceDashboardComponent implements OnInit, OnDestroy
|
||||
widget6: any = {};
|
||||
widget7: any = {};
|
||||
|
||||
dateNow = Date.now();
|
||||
|
||||
constructor(private projectsDashboardService: EcommerceDashboardService)
|
||||
{
|
||||
this.projects = this.projectsDashboardService.projects;
|
||||
|
||||
this.selectedProject = this.projects[0];
|
||||
|
||||
this.widgets = this.projectsDashboardService.widgets;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ElementRef, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { EcommerceOrderService } from './order.service';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
@@ -10,9 +10,6 @@ import 'rxjs/add/observable/fromEvent';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Order } from './order.model';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { FuseUtils } from '../../../../../core/fuseUtils';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Location } from '@angular/common';
|
||||
import { orderStatuses } from './order-statuses';
|
||||
|
||||
@Component({
|
||||
@@ -32,8 +29,6 @@ export class FuseEcommerceOrderComponent implements OnInit, OnDestroy
|
||||
constructor(
|
||||
private orderService: EcommerceOrderService,
|
||||
private formBuilder: FormBuilder,
|
||||
public snackBar: MatSnackBar,
|
||||
private location: Location
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ElementRef, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { EcommerceProductService } from './product.service';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<div class="dialog-content-wrapper">
|
||||
<mat-toolbar matDialogTitle class="mat-accent m-0">
|
||||
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<span class="title dialog-title">New Message</span>
|
||||
<button mat-button class="mat-icon-button"
|
||||
(click)="dialogRef.close()"
|
||||
aria-label="Close dialog">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
||||
<div mat-dialog-content class="p-24 m-0" fusePerfectScrollbar>
|
||||
|
||||
<form name="composeForm" [formGroup]="composeForm" class="compose-form" fxLayout="column" fxFlex>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput name="from"
|
||||
placeholder="From"
|
||||
formControlName="from"
|
||||
type="email">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput name="to"
|
||||
placeholder="To"
|
||||
formControlName="to"
|
||||
type="email" required>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput
|
||||
name="cc"
|
||||
placeholder="Cc"
|
||||
formControlName="cc"
|
||||
type="email">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput
|
||||
name="bcc"
|
||||
placeholder="Bcc"
|
||||
formControlName="bcc"
|
||||
type="email">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput name="subject"
|
||||
placeholder="Subject"
|
||||
formControlName="subject">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<textarea matInput name="message"
|
||||
placeholder="Message"
|
||||
formControlName="message"
|
||||
rows="6">
|
||||
</textarea>
|
||||
</mat-form-field>
|
||||
|
||||
<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 mat-icon-button aria-label="Delete attachment">
|
||||
<mat-icon class="s-16">close</mat-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 mat-icon-button aria-label="Delete attachment">
|
||||
<mat-icon class="s-16">close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div mat-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<div>
|
||||
<button mat-raised-button
|
||||
(click)="dialogRef.close(['send',composeForm])"
|
||||
class="save-button mat-accent"
|
||||
[disabled]="composeForm.invalid"
|
||||
aria-label="SAVE">
|
||||
SEND
|
||||
</button>
|
||||
|
||||
<button mat-icon-button matTooltip="Attach a file">
|
||||
<mat-icon>attach_file</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button mat-button
|
||||
class="mat-icon-button"
|
||||
(click)="dialogRef.close(['delete',composeForm])"
|
||||
aria-label="Delete"
|
||||
matTooltip="Delete">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
.mail-compose-dialog {
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
width: 720px;
|
||||
|
||||
.compose-form {
|
||||
|
||||
.mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-content-wrapper {
|
||||
max-height: 85vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-mail-compose',
|
||||
templateUrl : './compose.component.html',
|
||||
styleUrls : ['./compose.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class FuseMailNgrxComposeDialogComponent implements OnInit
|
||||
{
|
||||
composeForm: FormGroup;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<FuseMailNgrxComposeDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) private data: any,
|
||||
private formBuilder: FormBuilder
|
||||
)
|
||||
{
|
||||
this.composeForm = this.createComposeForm();
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
}
|
||||
|
||||
createComposeForm()
|
||||
{
|
||||
return this.formBuilder.group({
|
||||
from : {
|
||||
value : ['johndoe@creapond.com'],
|
||||
disabled: [true]
|
||||
},
|
||||
to : [''],
|
||||
cc : [''],
|
||||
bcc : [''],
|
||||
subject: [''],
|
||||
message: ['']
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
14
src/app/main/content/apps/mail-ngrx/i18n/en.ts
Normal file
14
src/app/main/content/apps/mail-ngrx/i18n/en.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export const locale = {
|
||||
lang: 'en',
|
||||
data: {
|
||||
'MAIL': {
|
||||
'COMPOSE' : 'COMPOSE',
|
||||
'FOLDERS' : 'FOLDERS',
|
||||
'FILTERS' : 'FILTERS',
|
||||
'LABELS' : 'LABELS',
|
||||
'NO_MESSAGES' : 'There are no messages!',
|
||||
'SELECT_A_MESSAGE_TO_READ': 'Select a message to read',
|
||||
'SEARCH_PLACEHOLDER': 'Search for an e-mail or contact'
|
||||
}
|
||||
}
|
||||
};
|
||||
14
src/app/main/content/apps/mail-ngrx/i18n/tr.ts
Normal file
14
src/app/main/content/apps/mail-ngrx/i18n/tr.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export const locale = {
|
||||
lang: 'tr',
|
||||
data: {
|
||||
'MAIL': {
|
||||
'COMPOSE' : 'YENİ E-POSTA',
|
||||
'FOLDERS' : 'KLASÖRLER',
|
||||
'FILTERS' : 'FİLTRELER',
|
||||
'LABELS' : 'ETİKETLER',
|
||||
'NO_MESSAGES' : 'Mesajiniz bulunmamakta!',
|
||||
'SELECT_A_MESSAGE_TO_READ': 'Okumak için bir mesaj seçin',
|
||||
'SEARCH_PLACEHOLDER' : 'E-mail yada bir kişi arayın'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,140 @@
|
||||
<div *ngIf="!mail" fxLayout="column" fxLayoutAlign="center center" fxFlex>
|
||||
<mat-icon class="s-128 mb-16 select-message-icon">
|
||||
email
|
||||
</mat-icon>
|
||||
<span class="select-message-text hint-text">
|
||||
<span>{{ 'MAIL.SELECT_A_MESSAGE_TO_READ' | translate }}</span>
|
||||
</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$ | async) | getById:labelId:'color'}"></div>
|
||||
<div class="label-title">{{(labels$ | async) | getById:labelId:'title'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions" fxLayout="row" fxLayoutAlign="start center">
|
||||
<button mat-button class="mat-icon-button" (click)="toggleStar($event)" aria-label="Toggle star">
|
||||
<mat-icon *ngIf="mail.starred">star</mat-icon>
|
||||
<mat-icon *ngIf="!mail.starred">star_outline</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-button class="mat-icon-button" (click)="toggleImportant($event)" aria-label="Toggle important">
|
||||
<mat-icon *ngIf="mail.important">label</mat-icon>
|
||||
<mat-icon *ngIf="!mail.important">label_outline</mat-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 mat-button [matMenuTriggerFor]="moreMenu" aria-label="More" class="mat-icon-button"
|
||||
(click)="$event.stopPropagation()">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
|
||||
<mat-menu #moreMenu="matMenu">
|
||||
<button mat-menu-item aria-label="Reply">
|
||||
<mat-icon>reply</mat-icon>
|
||||
<span>Reply</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item aria-label="Forward">
|
||||
<mat-icon>forward</mat-icon>
|
||||
<span>Forward</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item aria-label="Print">
|
||||
<mat-icon>print</mat-icon>
|
||||
<span>Print</span>
|
||||
</button>
|
||||
</mat-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>
|
||||
@@ -0,0 +1,118 @@
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Mail } from '../mail.model';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Store } from '@ngrx/store';
|
||||
import * as fromStore from '../store';
|
||||
import { MailNgrxService } from '../mail.service';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-mail-details',
|
||||
templateUrl : './mail-details.component.html',
|
||||
styleUrls : ['./mail-details.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FuseMailNgrxDetailsComponent implements OnInit, OnDestroy, OnChanges
|
||||
{
|
||||
labels$: Observable<any>;
|
||||
@Input('mail') mailInput: Mail;
|
||||
mail: Mail;
|
||||
showDetails = false;
|
||||
|
||||
constructor(
|
||||
private mailService: MailNgrxService,
|
||||
private store: Store<fromStore.MailAppState>
|
||||
)
|
||||
{
|
||||
this.labels$ = this.store.select(fromStore.getLabelsArr);
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
}
|
||||
|
||||
ngOnChanges()
|
||||
{
|
||||
this.updateModel(this.mailInput);
|
||||
this.markAsRead();
|
||||
}
|
||||
|
||||
markAsRead()
|
||||
{
|
||||
if ( this.mail && !this.mail.read )
|
||||
{
|
||||
this.mail.markRead();
|
||||
this.updateMail();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
toggleStar(event)
|
||||
{
|
||||
event.stopPropagation();
|
||||
this.mail.toggleStar();
|
||||
this.updateMail();
|
||||
}
|
||||
|
||||
toggleImportant(event)
|
||||
{
|
||||
event.stopPropagation();
|
||||
this.mail.toggleImportant();
|
||||
this.updateMail();
|
||||
}
|
||||
|
||||
updateModel(data)
|
||||
{
|
||||
this.mail = !data ? null : new Mail({...data});
|
||||
}
|
||||
|
||||
updateMail()
|
||||
{
|
||||
this.store.dispatch(new fromStore.UpdateMail(this.mail));
|
||||
this.updateModel(this.mail);
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
<mat-checkbox [checked]="selected" (change)="onSelectedChange()"
|
||||
(click)="$event.stopPropagation();">
|
||||
</mat-checkbox>
|
||||
|
||||
<div class="info" fxFlex FlexLayout="column">
|
||||
|
||||
<div class="row-1" fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
<div class="name" fxLayout="row" fxLayoutAlign="start center" fxFlex>
|
||||
<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>
|
||||
<mat-icon *ngIf="mail.hasAttachments">attachment</mat-icon>
|
||||
</div>
|
||||
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<div class="time">{{mail.time}}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row-2" fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
<div fxLayout="column" fxLayoutAlign="start start">
|
||||
|
||||
<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 fxHide fxShow.gt-sm>
|
||||
<div class="label" *ngFor="let labelId of mail.labels"
|
||||
fxLayout="row" fxLayoutAlign="start center">
|
||||
<div class="label-color"
|
||||
[ngStyle]="{'background-color': (labels$ | async) | getById:labelId:'color'}"></div>
|
||||
<div class="label-title">{{(labels$ | async) | getById:labelId:'title'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,130 @@
|
||||
@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;
|
||||
background: #FAFAFA;
|
||||
|
||||
&.unread {
|
||||
background: #FFFFFF;
|
||||
|
||||
.info {
|
||||
|
||||
.name {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.row-2 {
|
||||
|
||||
.subject {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.labels {
|
||||
background: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: #FFF8E1;
|
||||
|
||||
.info {
|
||||
|
||||
.row-2 {
|
||||
|
||||
.labels {
|
||||
background: #FFF8E1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.current-mail {
|
||||
background: #E3F2FD;
|
||||
|
||||
.info {
|
||||
|
||||
.row-2 {
|
||||
|
||||
.labels {
|
||||
background: #E3F2FD;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
overflow: hidden;
|
||||
width: 0;
|
||||
margin-left: 16px;
|
||||
position: relative;
|
||||
|
||||
.row-1 {
|
||||
margin-bottom: 8px;
|
||||
|
||||
.name {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
|
||||
.avatar {
|
||||
min-width: 32px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
background-color: mat-color($accent);
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-left: 4px;
|
||||
|
||||
.mat-icon-button {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row-2 {
|
||||
|
||||
.subject,
|
||||
.message {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.message {
|
||||
position: relative;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
|
||||
.labels {
|
||||
position: absolute;
|
||||
background: #FFFFFF;
|
||||
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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Mail } from '../../mail.model';
|
||||
import { MailNgrxService } from '../../mail.service';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import * as fromStore from '../../store';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-mail-list-item',
|
||||
templateUrl : './mail-list-item.component.html',
|
||||
styleUrls : ['./mail-list-item.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FuseMailNgrxListItemComponent implements OnInit, OnDestroy
|
||||
{
|
||||
@Input() mail: Mail;
|
||||
@HostBinding('class.selected') selected: boolean;
|
||||
@HostBinding('class.unread') unread: boolean;
|
||||
labels$: Observable<any>;
|
||||
selectedMailIds$: Observable<any>;
|
||||
|
||||
constructor(
|
||||
private mailService: MailNgrxService,
|
||||
private store: Store<fromStore.MailAppState>,
|
||||
private cd: ChangeDetectorRef
|
||||
)
|
||||
{
|
||||
this.labels$ = this.store.select(fromStore.getLabelsArr);
|
||||
this.selectedMailIds$ = this.store.select(fromStore.getSelectedMailIds);
|
||||
this.selected = false;
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
// Set the initial values
|
||||
this.mail = new Mail(this.mail);
|
||||
this.unread = !this.mail.read;
|
||||
|
||||
this.selectedMailIds$.subscribe((selectedMailIds) => {
|
||||
this.selected = selectedMailIds.length > 0 && selectedMailIds.find(id => id === this.mail.id) !== undefined;
|
||||
this.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
refresh()
|
||||
{
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
|
||||
onSelectedChange()
|
||||
{
|
||||
this.store.dispatch(new fromStore.ToggleInSelectedMails(this.mail.id));
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<div *ngIf="mails.length === 0" fxLayout="column" fxLayoutAlign="center center" fxFlexFill>
|
||||
<span class="no-messages-text hint-text">{{ 'MAIL.NO_MESSAGES' | translate }}</span>
|
||||
</div>
|
||||
|
||||
<div class="mail-list">
|
||||
<fuse-mail-list-item matRipple *ngFor="let mail of mails" [mail]="mail" (click)="readMail(mail.id)"
|
||||
[ngClass]="{'current-mail':mail?.id == currentMail?.id}">
|
||||
</fuse-mail-list-item>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
: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;
|
||||
}
|
||||
|
||||
.mail-list {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Mail } from '../mail.model';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { MailNgrxService } from '../mail.service';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-mail-list',
|
||||
templateUrl : './mail-list.component.html',
|
||||
styleUrls : ['./mail-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FuseMailNgrxListComponent implements OnInit, OnDestroy
|
||||
{
|
||||
@Input() mails: Mail[];
|
||||
@Input() currentMail: Mail[];
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private mailService: MailNgrxService,
|
||||
private router: Router
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.router.navigate(['apps/mail-ngrx/label/' + labelHandle + '/' + mailId]);
|
||||
}
|
||||
else if ( filterHandle )
|
||||
{
|
||||
this.router.navigate(['apps/mail-ngrx/filter/' + filterHandle + '/' + mailId]);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.router.navigate(['apps/mail-ngrx/' + folderHandle + '/' + mailId]);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
}
|
||||
}
|
||||
116
src/app/main/content/apps/mail-ngrx/mail.component.html
Normal file
116
src/app/main/content/apps/mail-ngrx/mail.component.html
Normal file
@@ -0,0 +1,116 @@
|
||||
<div id="mail" class="page-layout carded left-sidenav" fusePerfectScrollbar>
|
||||
|
||||
<!-- TOP BACKGROUND -->
|
||||
<div class="top-bg mat-accent-bg"></div>
|
||||
<!-- / TOP BACKGROUND -->
|
||||
|
||||
<mat-sidenav-container>
|
||||
|
||||
<!-- SIDENAV -->
|
||||
<mat-sidenav class="sidenav mat-sidenav-opened" align="start" mode="side" opened="true"
|
||||
fuseMatSidenavHelper="carded-left-sidenav" mat-is-locked-open="gt-md">
|
||||
<fuse-mail-main-sidenav></fuse-mail-main-sidenav>
|
||||
</mat-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 mat-button class="mat-icon-button sidenav-toggle"
|
||||
fuseMatSidenavToggler="carded-left-sidenav"
|
||||
fxHide.gt-md aria-label="Toggle Sidenav">
|
||||
<mat-icon>menu</mat-icon>
|
||||
</button>
|
||||
|
||||
<div class="search mat-white-bg" flex fxLayout="row" fxLayoutAlign="start center">
|
||||
<mat-icon>search</mat-icon>
|
||||
<input [formControl]="searchInput" [placeholder]="'MAIL.SEARCH_PLACEHOLDER' | translate" fxFlex>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / CONTENT HEADER -->
|
||||
|
||||
<!-- CONTENT CARD -->
|
||||
<div class="content-card mat-white-bg" [ngClass]="{'current-mail-selected':currentMail$ | async}">
|
||||
|
||||
<!-- CONTENT TOOLBAR -->
|
||||
<div class="toolbar px-24 py-8">
|
||||
|
||||
<div class="mail-selection" fxFlex="row" fxLayoutAlign="start center">
|
||||
|
||||
<mat-checkbox (click)="toggleSelectAll($event)"
|
||||
[checked]="hasSelectedMails"
|
||||
[indeterminate]="isIndeterminate">
|
||||
</mat-checkbox>
|
||||
|
||||
<button mat-icon-button [matMenuTriggerFor]="selectMenu">
|
||||
<mat-icon>arrow_drop_down</mat-icon>
|
||||
</button>
|
||||
<mat-menu #selectMenu="matMenu">
|
||||
<button mat-menu-item (click)="selectAllMails()">All</button>
|
||||
<button mat-menu-item (click)="deselectAllMails()">None</button>
|
||||
<button mat-menu-item (click)="selectMailsByParameter('read', true)">Read</button>
|
||||
<button mat-menu-item (click)="selectMailsByParameter('read', false)">Unread</button>
|
||||
<button mat-menu-item (click)="selectMailsByParameter('starred', true)">Starred</button>
|
||||
<button mat-menu-item (click)="selectMailsByParameter('starred', false)">Unstarred</button>
|
||||
<button mat-menu-item (click)="selectMailsByParameter('important', true)">Important</button>
|
||||
<button mat-menu-item (click)="selectMailsByParameter('important', false)">Unimportant</button>
|
||||
</mat-menu>
|
||||
|
||||
<div class="toolbar-separator" *ngIf="hasSelectedMails"></div>
|
||||
|
||||
<button mat-icon-button (click)="setFolderOnSelectedMails(4)" *ngIf="hasSelectedMails">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-icon-button [matMenuTriggerFor]="folderMenu" *ngIf="hasSelectedMails">
|
||||
<mat-icon>folder</mat-icon>
|
||||
</button>
|
||||
<mat-menu #folderMenu="matMenu">
|
||||
<button mat-menu-item *ngFor="let folder of folders$ | async"
|
||||
(click)="setFolderOnSelectedMails(folder.id)">{{folder.title}}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<button mat-icon-button [matMenuTriggerFor]="labelMenu" *ngIf="hasSelectedMails">
|
||||
<mat-icon>label</mat-icon>
|
||||
</button>
|
||||
<mat-menu #labelMenu="matMenu">
|
||||
<button mat-menu-item *ngFor="let label of labels$ | async"
|
||||
(click)="toggleLabelOnSelectedMails(label.id)">{{label.title}}
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
||||
<div *ngIf="currentMail$ | async" fxHide.gt-xs>
|
||||
<button mat-icon-button (click)="deSelectCurrentMail()">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / CONTENT TOOLBAR -->
|
||||
|
||||
<!-- CONTENT -->
|
||||
<div class="content" fxLayoutAlign="row">
|
||||
|
||||
<fuse-mail-list fusePerfectScrollbar fxFlex [mails]="mails$ | async" [currentMail]="currentMail$ | async"></fuse-mail-list>
|
||||
|
||||
<fuse-mail-details [mail]="currentMail$ | async" fusePerfectScrollbar fxFlex></fuse-mail-details>
|
||||
|
||||
</div>
|
||||
<!-- / CONTENT -->
|
||||
|
||||
</div>
|
||||
<!-- / CONTENT CARD -->
|
||||
|
||||
</div>
|
||||
<!-- / CENTER -->
|
||||
|
||||
</mat-sidenav-container>
|
||||
|
||||
</div>
|
||||
81
src/app/main/content/apps/mail-ngrx/mail.component.scss
Normal file
81
src/app/main/content/apps/mail-ngrx/mail.component.scss
Normal file
@@ -0,0 +1,81 @@
|
||||
@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 {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
fuse-mail-list,
|
||||
fuse-mail-details {
|
||||
flex: 1 0 100%;
|
||||
}
|
||||
|
||||
fuse-mail-details {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&.current-mail-selected {
|
||||
|
||||
.toolbar {
|
||||
padding-left: 16px !important;
|
||||
|
||||
.mail-selection {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
fuse-mail-list {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
fuse-mail-details {
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
133
src/app/main/content/apps/mail-ngrx/mail.component.ts
Normal file
133
src/app/main/content/apps/mail-ngrx/mail.component.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { MailNgrxService } from './mail.service';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { Mail } from './mail.model';
|
||||
import { FuseTranslationLoaderService } from '../../../../core/services/translation-loader.service';
|
||||
import { locale as english } from './i18n/en';
|
||||
import { locale as turkish } from './i18n/tr';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import * as fromStore from './store';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-mail',
|
||||
templateUrl : './mail.component.html',
|
||||
styleUrls : ['./mail.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FuseMailNgrxComponent implements OnInit, OnDestroy
|
||||
{
|
||||
hasSelectedMails: boolean;
|
||||
isIndeterminate: boolean;
|
||||
searchInput: FormControl;
|
||||
mails$: Observable<any>;
|
||||
folders$: Observable<any>;
|
||||
labels$: Observable<any>;
|
||||
currentMail$: Observable<Mail>;
|
||||
selectedMailIds$: Observable<string[]>;
|
||||
searchText$: Observable<string>;
|
||||
mails: Mail[];
|
||||
selectedMailIds: string[];
|
||||
|
||||
constructor(
|
||||
private mailService: MailNgrxService,
|
||||
private translationLoader: FuseTranslationLoaderService,
|
||||
private store: Store<fromStore.MailAppState>,
|
||||
private cd: ChangeDetectorRef
|
||||
)
|
||||
{
|
||||
this.searchInput = new FormControl('');
|
||||
this.translationLoader.loadTranslations(english, turkish);
|
||||
this.currentMail$ = this.store.select(fromStore.getCurrentMail);
|
||||
this.mails$ = this.store.select(fromStore.getMailsArr);
|
||||
this.folders$ = this.store.select(fromStore.getFoldersArr);
|
||||
this.labels$ = this.store.select(fromStore.getLabelsArr);
|
||||
this.selectedMailIds$ = this.store.select(fromStore.getSelectedMailIds);
|
||||
this.searchText$ = this.store.select(fromStore.getSearchText);
|
||||
this.mails = [];
|
||||
this.selectedMailIds = [];
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.mails$.subscribe(mails => {
|
||||
this.mails = mails;
|
||||
});
|
||||
|
||||
this.selectedMailIds$
|
||||
.subscribe(selectedMailIds => {
|
||||
this.selectedMailIds = selectedMailIds;
|
||||
this.hasSelectedMails = selectedMailIds.length > 0;
|
||||
this.isIndeterminate = (selectedMailIds.length !== this.mails.length && selectedMailIds.length > 0);
|
||||
this.refresh();
|
||||
});
|
||||
|
||||
this.searchText$.subscribe(searchText => {
|
||||
this.searchInput.setValue(searchText);
|
||||
});
|
||||
|
||||
this.searchInput.valueChanges
|
||||
.debounceTime(300)
|
||||
.distinctUntilChanged()
|
||||
.subscribe(searchText => {
|
||||
this.store.dispatch(new fromStore.SetSearchText(searchText));
|
||||
});
|
||||
}
|
||||
|
||||
toggleSelectAll(ev)
|
||||
{
|
||||
ev.preventDefault();
|
||||
|
||||
if ( this.selectedMailIds.length && this.selectedMailIds.length > 0 )
|
||||
{
|
||||
this.deselectAllMails();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.selectAllMails();
|
||||
}
|
||||
}
|
||||
|
||||
selectAllMails()
|
||||
{
|
||||
this.store.dispatch(new fromStore.SelectAllMails());
|
||||
}
|
||||
|
||||
deselectAllMails()
|
||||
{
|
||||
this.store.dispatch(new fromStore.DeselectAllMails());
|
||||
}
|
||||
|
||||
selectMailsByParameter(parameter, value)
|
||||
{
|
||||
this.store.dispatch(new fromStore.SelectMailsByParameter({
|
||||
parameter,
|
||||
value
|
||||
}));
|
||||
}
|
||||
|
||||
toggleLabelOnSelectedMails(labelId)
|
||||
{
|
||||
this.store.dispatch(new fromStore.AddLabelOnSelectedMails(labelId));
|
||||
}
|
||||
|
||||
setFolderOnSelectedMails(folderId)
|
||||
{
|
||||
this.store.dispatch(new fromStore.SetFolderOnSelectedMails(folderId));
|
||||
}
|
||||
|
||||
deSelectCurrentMail()
|
||||
{
|
||||
this.store.dispatch(new fromStore.SetCurrentMail(''));
|
||||
}
|
||||
|
||||
refresh()
|
||||
{
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
this.cd.detach();
|
||||
}
|
||||
}
|
||||
66
src/app/main/content/apps/mail-ngrx/mail.model.ts
Normal file
66
src/app/main/content/apps/mail-ngrx/mail.model.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
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;
|
||||
}
|
||||
|
||||
markRead()
|
||||
{
|
||||
this.read = true;
|
||||
}
|
||||
|
||||
markUnRead()
|
||||
{
|
||||
this.read = false;
|
||||
}
|
||||
}
|
||||
73
src/app/main/content/apps/mail-ngrx/mail.module.ts
Normal file
73
src/app/main/content/apps/mail-ngrx/mail.module.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SharedModule } from '../../../../core/modules/shared.module';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { FuseMailNgrxComponent } from './mail.component';
|
||||
import { FuseMailNgrxMainSidenavComponent } from './sidenavs/main/main-sidenav.component';
|
||||
import { FuseMailNgrxListItemComponent } from './mail-list/mail-list-item/mail-list-item.component';
|
||||
import { FuseMailNgrxListComponent } from './mail-list/mail-list.component';
|
||||
import { FuseMailNgrxDetailsComponent } from './mail-details/mail-details.component';
|
||||
import { MailNgrxService } from './mail.service';
|
||||
import { FuseMailNgrxComposeDialogComponent } from './dialogs/compose/compose.component';
|
||||
import { MailAppStoreModule } from './store/store.module';
|
||||
import * as fromGuards from './store/guards/index';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path : 'label/:labelHandle',
|
||||
component : FuseMailNgrxComponent,
|
||||
canActivate: [fromGuards.ResolveGuard]
|
||||
},
|
||||
{
|
||||
path : 'label/:labelHandle/:mailId',
|
||||
component : FuseMailNgrxComponent,
|
||||
canActivate: [fromGuards.ResolveGuard]
|
||||
},
|
||||
{
|
||||
path : 'filter/:filterHandle',
|
||||
component: FuseMailNgrxComponent,
|
||||
canActivate: [fromGuards.ResolveGuard]
|
||||
},
|
||||
{
|
||||
path : 'filter/:filterHandle/:mailId',
|
||||
component: FuseMailNgrxComponent,
|
||||
canActivate: [fromGuards.ResolveGuard]
|
||||
},
|
||||
{
|
||||
path : ':folderHandle',
|
||||
component: FuseMailNgrxComponent,
|
||||
canActivate: [fromGuards.ResolveGuard]
|
||||
},
|
||||
{
|
||||
path : ':folderHandle/:mailId',
|
||||
component: FuseMailNgrxComponent,
|
||||
canActivate: [fromGuards.ResolveGuard]
|
||||
},
|
||||
{
|
||||
path : '**',
|
||||
redirectTo: 'inbox'
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations : [
|
||||
FuseMailNgrxComponent,
|
||||
FuseMailNgrxListComponent,
|
||||
FuseMailNgrxListItemComponent,
|
||||
FuseMailNgrxDetailsComponent,
|
||||
FuseMailNgrxMainSidenavComponent,
|
||||
FuseMailNgrxComposeDialogComponent
|
||||
],
|
||||
imports : [
|
||||
SharedModule,
|
||||
RouterModule.forChild(routes),
|
||||
MailAppStoreModule
|
||||
],
|
||||
providers : [
|
||||
MailNgrxService,
|
||||
fromGuards.ResolveGuard
|
||||
],
|
||||
entryComponents: [FuseMailNgrxComposeDialogComponent]
|
||||
})
|
||||
export class FuseMailNgrxModule
|
||||
{
|
||||
}
|
||||
87
src/app/main/content/apps/mail-ngrx/mail.service.ts
Normal file
87
src/app/main/content/apps/mail-ngrx/mail.service.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Mail } from './mail.model';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { MailAppState } from './store/reducers';
|
||||
import { getFiltersArr, getFoldersArr, getLabelsArr, getMailsArr } from './store/selectors';
|
||||
|
||||
@Injectable()
|
||||
export class MailNgrxService
|
||||
{
|
||||
foldersArr: any;
|
||||
filtersArr: any;
|
||||
labelsArr: any;
|
||||
selectedMails: Mail[];
|
||||
mails: Mail[];
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private store: Store<MailAppState>
|
||||
)
|
||||
{
|
||||
this.store.select(getFoldersArr).subscribe(folders => {
|
||||
this.foldersArr = folders;
|
||||
});
|
||||
this.store.select(getFiltersArr).subscribe(filters => {
|
||||
this.filtersArr = filters;
|
||||
});
|
||||
this.store.select(getLabelsArr).subscribe(labels => {
|
||||
this.labelsArr = labels;
|
||||
});
|
||||
this.store.select(getMailsArr).subscribe(mails => {
|
||||
this.mails = mails;
|
||||
});
|
||||
|
||||
this.selectedMails = [];
|
||||
}
|
||||
|
||||
getAllMails(): Observable<Mail[]>
|
||||
{
|
||||
return this.http.get<Mail[]>('api/mail-mails');
|
||||
}
|
||||
|
||||
getFolders(): Observable<any>
|
||||
{
|
||||
return this.http.get('api/mail-folders');
|
||||
}
|
||||
|
||||
getFilters(): Observable<any>
|
||||
{
|
||||
return this.http.get('api/mail-filters');
|
||||
}
|
||||
|
||||
getLabels(): Observable<any>
|
||||
{
|
||||
return this.http.get('api/mail-labels');
|
||||
}
|
||||
|
||||
getMails(handle): Observable<Mail[]>
|
||||
{
|
||||
if ( handle.id === 'labelHandle' )
|
||||
{
|
||||
const labelId = this.labelsArr.find(label => label.handle === handle.value).id;
|
||||
return this.http.get<Mail[]>('api/mail-mails?labels=' + labelId);
|
||||
}
|
||||
else if ( handle.id === 'filterHandle' )
|
||||
{
|
||||
return this.http.get<Mail[]>('api/mail-mails?' + handle.value + '=true');
|
||||
}
|
||||
else // folderHandle
|
||||
{
|
||||
const folderId = this.foldersArr.find(folder => folder.handle === handle.value).id;
|
||||
return this.http.get<any>('api/mail-mails?folder=' + folderId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the mail
|
||||
* @param mail
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
updateMail(mail)
|
||||
{
|
||||
return this.http.post('api/mail-mails/' + mail.id, {...mail});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<!-- SIDENAV HEADER -->
|
||||
<div fxLayout="column" fxLayoutAlign="space-between start"
|
||||
class="header p-24 pb-4" ngClass="mat-accent-bg" ngClass.gt-md="white-fg">
|
||||
|
||||
<div class="logo" fxFlex fxLayout="row" fxLayoutAlign="start center">
|
||||
<mat-icon class="logo-icon s-32">mail</mat-icon>
|
||||
<span class="logo-text">Mailbox Ngrx</span>
|
||||
</div>
|
||||
|
||||
<div class="account" fxLayout="column">
|
||||
<div class="title">John Doe</div>
|
||||
<mat-form-field floatPlaceholder="never">
|
||||
<mat-select class="account-selection" placeholder="Mail Selection"
|
||||
[ngModel]="selectedAccount">
|
||||
<mat-option *ngFor="let account of (accounts | keys)" [value]="account.key">
|
||||
{{account.value}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- / SIDENAV HEADER -->
|
||||
|
||||
<!-- SIDENAV CONTENT -->
|
||||
<div class="content" fusePerfectScrollbar>
|
||||
|
||||
<div class="p-24">
|
||||
<button mat-raised-button fxFlex
|
||||
class="mat-accent compose-dialog-button"
|
||||
(click)="composeDialog()"
|
||||
aria-label="Compose">
|
||||
{{ 'MAIL.COMPOSE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="nav">
|
||||
|
||||
<div class="nav-subheader">{{ 'MAIL.FOLDERS' | translate }}</div>
|
||||
|
||||
<div class="nav-item" *ngFor="let folder of (folders$ | async)">
|
||||
<a class="nav-link" matRipple [routerLink]="'/apps/mail-ngrx/' + folder.handle" routerLinkActive="active">
|
||||
<mat-icon class="nav-link-icon" *ngIf="folder.icon">{{folder.icon}}</mat-icon>
|
||||
<span>{{folder.title}}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-subheader">{{ 'MAIL.FILTERS' | translate }}</div>
|
||||
|
||||
<div class="nav-item" *ngFor="let filter of (filters$ | async)">
|
||||
<a class="nav-link" matRipple [routerLink]="'/apps/mail-ngrx/filter/' + filter.handle" routerLinkActive="active">
|
||||
<mat-icon class="nav-link-icon" *ngIf="filter.icon">{{filter.icon}}</mat-icon>
|
||||
<span>{{filter.title}}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-subheader">{{ 'MAIL.LABELS' | translate }}</div>
|
||||
|
||||
<div class="nav-item" *ngFor="let label of (labels$ | async)">
|
||||
<a class="nav-link" matRipple [routerLink]="'/apps/mail-ngrx/label/' + label.handle" routerLinkActive="active">
|
||||
<mat-icon class="nav-link-icon" [ngStyle]="{'color':label.color}">label</mat-icon>
|
||||
<span>{{label.title}}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- / SIDENAV CONTENT -->
|
||||
@@ -0,0 +1,30 @@
|
||||
: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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { FuseMailNgrxComposeDialogComponent } from '../../dialogs/compose/compose.component';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Store } from '@ngrx/store';
|
||||
import * as fromStore from './../../store';
|
||||
import { MailNgrxService } from '../../mail.service';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-mail-main-sidenav',
|
||||
templateUrl : './main-sidenav.component.html',
|
||||
styleUrls : ['./main-sidenav.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FuseMailNgrxMainSidenavComponent implements OnInit, OnDestroy
|
||||
{
|
||||
labels: any[];
|
||||
accounts: object;
|
||||
selectedAccount: string;
|
||||
dialogRef: any;
|
||||
|
||||
folders$: Observable<any>;
|
||||
filters$: Observable<any>;
|
||||
labels$: Observable<any>;
|
||||
|
||||
constructor(
|
||||
private mailService: MailNgrxService,
|
||||
public dialog: MatDialog,
|
||||
private store: Store<fromStore.MailAppState>
|
||||
)
|
||||
{
|
||||
// Data
|
||||
this.accounts = {
|
||||
'creapond' : 'johndoe@creapond.com',
|
||||
'withinpixels': 'johndoe@withinpixels.com'
|
||||
};
|
||||
|
||||
this.selectedAccount = 'creapond';
|
||||
|
||||
this.folders$ = this.store.select(fromStore.getFoldersArr);
|
||||
this.filters$ = this.store.select(fromStore.getFiltersArr);
|
||||
this.labels$ = this.store.select(fromStore.getLabelsArr);
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
}
|
||||
|
||||
composeDialog()
|
||||
{
|
||||
this.dialogRef = this.dialog.open(FuseMailNgrxComposeDialogComponent, {
|
||||
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()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
|
||||
export const GET_FILTERS = '[FILTERS] GET FILTERS';
|
||||
export const GET_FILTERS_SUCCESS = '[FILTERS] GET FILTERS SUCCESS';
|
||||
export const GET_FILTERS_FAILED = '[FILTERS] GET FILTERS FAILED';
|
||||
|
||||
/**
|
||||
* Get Filters
|
||||
*/
|
||||
export class GetFilters implements Action
|
||||
{
|
||||
readonly type = GET_FILTERS;
|
||||
|
||||
constructor(public payload: any)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Filters Success
|
||||
*/
|
||||
export class GetFiltersSuccess implements Action
|
||||
{
|
||||
readonly type = GET_FILTERS_SUCCESS;
|
||||
|
||||
constructor(public payload: any)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Filters Failed
|
||||
*/
|
||||
export class GetFiltersFailed implements Action
|
||||
{
|
||||
readonly type = GET_FILTERS_FAILED;
|
||||
|
||||
constructor(public payload: string)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
export type FiltersActionsAll
|
||||
= GetFilters
|
||||
| GetFiltersSuccess
|
||||
| GetFiltersFailed;
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
|
||||
export const GET_FOLDERS = '[FOLDERS] GET FOLDERS';
|
||||
export const GET_FOLDERS_SUCCESS = '[FOLDERS] GET FOLDERS SUCCESS';
|
||||
export const GET_FOLDERS_FAILED = '[FOLDERS] GET FOLDERS FAILED';
|
||||
|
||||
/**
|
||||
* Get Folders
|
||||
*/
|
||||
export class GetFolders implements Action
|
||||
{
|
||||
readonly type = GET_FOLDERS;
|
||||
|
||||
constructor(public payload: any)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Folders Success
|
||||
*/
|
||||
export class GetFoldersSuccess implements Action
|
||||
{
|
||||
readonly type = GET_FOLDERS_SUCCESS;
|
||||
|
||||
constructor(public payload: any)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Folders Failed
|
||||
*/
|
||||
export class GetFoldersFailed implements Action
|
||||
{
|
||||
readonly type = GET_FOLDERS_FAILED;
|
||||
|
||||
constructor(public payload: string)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
export type FoldersActionsAll
|
||||
= GetFolders
|
||||
| GetFoldersSuccess
|
||||
| GetFoldersFailed;
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './mails.actions';
|
||||
export * from './folders.actions';
|
||||
export * from './filters.actions';
|
||||
export * from './labels.actions';
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
|
||||
export const GET_LABELS = '[LABELS] GET LABELS';
|
||||
export const GET_LABELS_SUCCESS = '[LABELS] GET LABELS SUCCESS';
|
||||
export const GET_LABELS_FAILED = '[LABELS] GET LABELS FAILED';
|
||||
|
||||
/**
|
||||
* Get Labels
|
||||
*/
|
||||
export class GetLabels implements Action
|
||||
{
|
||||
readonly type = GET_LABELS;
|
||||
|
||||
constructor(public payload: any)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Labels Success
|
||||
*/
|
||||
export class GetLabelsSuccess implements Action
|
||||
{
|
||||
readonly type = GET_LABELS_SUCCESS;
|
||||
|
||||
constructor(public payload: any)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Labels Failed
|
||||
*/
|
||||
export class GetLabelsFailed implements Action
|
||||
{
|
||||
readonly type = GET_LABELS_FAILED;
|
||||
|
||||
constructor(public payload: string)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
export type LabelsActionsAll
|
||||
= GetLabels
|
||||
| GetLabelsSuccess
|
||||
| GetLabelsFailed;
|
||||
@@ -0,0 +1,243 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { Mail } from '../../mail.model';
|
||||
|
||||
export const GET_MAILS = '[MAILS] GET MAILS';
|
||||
export const GET_MAILS_SUCCESS = '[MAILS] GET MAILS SUCCESS';
|
||||
export const GET_MAILS_FAILED = '[MAILS] GET MAILS FAILED';
|
||||
export const SET_CURRENT_MAIL = '[MAILS] SET CURRENT MAIL';
|
||||
export const SET_CURRENT_MAIL_SUCCESS = '[MAILS] SET CURRENT MAIL SUCCESS';
|
||||
export const CHECK_CURRENT_MAIL = '[MAILS] CHECK CURRENT MAIL';
|
||||
export const UPDATE_MAIL = '[MAILS] UPDATE MAIL';
|
||||
export const UPDATE_MAIL_SUCCESS = '[MAILS] UPDATE MAIL SUCCESS';
|
||||
export const UPDATE_MAILS = '[MAILS] UPDATE MAILS';
|
||||
export const UPDATE_MAILS_SUCCESS = '[MAILS] UPDATE MAILS SUCCESS';
|
||||
export const SET_SEARCH_TEXT = '[MAILS] SET SEARCH TEXT';
|
||||
export const SELECT_ALL_MAILS = '[MAILS] SELECT ALL MAILS';
|
||||
export const DESELECT_ALL_MAILS = '[MAILS] DESELECT ALL MAILS';
|
||||
export const TOGGLE_IN_SELECTED_MAILS = '[MAILS] TOGGLE IN SELECTED MAILS';
|
||||
export const SELECT_MAILS_BY_PARAMETER = '[MAILS] SELECT MAILS BY PARAMETER';
|
||||
export const SET_FOLDER_ON_SELECTED_MAILS = '[MAILS] SET FOLDER ON SELECTED MAILS';
|
||||
export const ADD_LABEL_ON_SELECTED_MAILS = '[MAILS] ADD LABEL ON SELECTED MAILS';
|
||||
|
||||
/**
|
||||
* Get Mails
|
||||
*/
|
||||
export class GetMails implements Action
|
||||
{
|
||||
readonly type = GET_MAILS;
|
||||
|
||||
constructor()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Mails Success
|
||||
*/
|
||||
export class GetMailsSuccess implements Action
|
||||
{
|
||||
readonly type = GET_MAILS_SUCCESS;
|
||||
|
||||
constructor(public payload: any)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Mails Failed
|
||||
*/
|
||||
export class GetMailsFailed implements Action
|
||||
{
|
||||
readonly type = GET_MAILS_FAILED;
|
||||
|
||||
constructor(public payload: string)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Current Mail
|
||||
*/
|
||||
export class SetCurrentMail implements Action
|
||||
{
|
||||
readonly type = SET_CURRENT_MAIL;
|
||||
|
||||
constructor(public payload: string)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Current Mail Success
|
||||
*/
|
||||
export class SetCurrentMailSuccess implements Action
|
||||
{
|
||||
readonly type = SET_CURRENT_MAIL_SUCCESS;
|
||||
|
||||
constructor(public payload: any)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Current Mail
|
||||
*/
|
||||
export class CheckCurrentMail implements Action
|
||||
{
|
||||
readonly type = CHECK_CURRENT_MAIL;
|
||||
|
||||
constructor()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Mail
|
||||
*/
|
||||
export class UpdateMail implements Action
|
||||
{
|
||||
readonly type = UPDATE_MAIL;
|
||||
|
||||
constructor(public payload: any)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Mail Success
|
||||
*/
|
||||
export class UpdateMailSuccess implements Action
|
||||
{
|
||||
readonly type = UPDATE_MAIL_SUCCESS;
|
||||
|
||||
constructor(public payload: Mail)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Mails
|
||||
*/
|
||||
export class UpdateMails implements Action
|
||||
{
|
||||
readonly type = UPDATE_MAILS;
|
||||
|
||||
constructor(public payload: Mail[])
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Mails Success
|
||||
*/
|
||||
export class UpdateMailsSuccess implements Action
|
||||
{
|
||||
readonly type = UPDATE_MAILS_SUCCESS;
|
||||
|
||||
constructor()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Search Text
|
||||
*/
|
||||
export class SetSearchText implements Action
|
||||
{
|
||||
readonly type = SET_SEARCH_TEXT;
|
||||
|
||||
constructor(public payload: string)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select All Mails
|
||||
*/
|
||||
export class SelectAllMails implements Action
|
||||
{
|
||||
readonly type = SELECT_ALL_MAILS;
|
||||
|
||||
constructor()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deselect All Mails
|
||||
*/
|
||||
export class DeselectAllMails implements Action
|
||||
{
|
||||
readonly type = DESELECT_ALL_MAILS;
|
||||
|
||||
constructor()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle In Selected Mails
|
||||
*/
|
||||
export class ToggleInSelectedMails implements Action
|
||||
{
|
||||
readonly type = TOGGLE_IN_SELECTED_MAILS;
|
||||
|
||||
constructor(public payload: string)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select Mails by Parameter
|
||||
*/
|
||||
export class SelectMailsByParameter implements Action
|
||||
{
|
||||
readonly type = SELECT_MAILS_BY_PARAMETER;
|
||||
|
||||
constructor(public payload: any)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Folder on Selected Mails
|
||||
*/
|
||||
export class SetFolderOnSelectedMails implements Action
|
||||
{
|
||||
readonly type = SET_FOLDER_ON_SELECTED_MAILS;
|
||||
|
||||
constructor(public payload: string)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add label on Selected Mails
|
||||
*/
|
||||
export class AddLabelOnSelectedMails implements Action
|
||||
{
|
||||
readonly type = ADD_LABEL_ON_SELECTED_MAILS;
|
||||
|
||||
constructor(public payload: string)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
export type MailsActionsAll
|
||||
= GetMails
|
||||
| GetMailsSuccess
|
||||
| GetMailsFailed
|
||||
| SetCurrentMail
|
||||
| SetCurrentMailSuccess
|
||||
| CheckCurrentMail
|
||||
| UpdateMail
|
||||
| UpdateMailSuccess
|
||||
| UpdateMails
|
||||
| UpdateMailsSuccess
|
||||
| SetSearchText
|
||||
| SelectAllMails
|
||||
| DeselectAllMails
|
||||
| ToggleInSelectedMails
|
||||
| SelectMailsByParameter
|
||||
| SetFolderOnSelectedMails
|
||||
| AddLabelOnSelectedMails;
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Actions, Effect } from '@ngrx/effects';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/delay';
|
||||
import 'rxjs/add/operator/map';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||
import * as FiltersActions from '../actions/filters.actions';
|
||||
import { MailNgrxService } from '../../mail.service';
|
||||
|
||||
@Injectable()
|
||||
export class FiltersEffect
|
||||
{
|
||||
constructor(
|
||||
private actions: Actions,
|
||||
private mailService: MailNgrxService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filters from Server
|
||||
* @type {Observable<any>}
|
||||
*/
|
||||
@Effect()
|
||||
getFilters: Observable<FiltersActions.FiltersActionsAll> =
|
||||
this.actions
|
||||
.ofType<FiltersActions.GetFilters>(FiltersActions.GET_FILTERS)
|
||||
.pipe(
|
||||
switchMap((action) => {
|
||||
return this.mailService.getFilters()
|
||||
.pipe(
|
||||
map((filters: any) => {
|
||||
return new FiltersActions.GetFiltersSuccess(filters);
|
||||
}),
|
||||
catchError(err => of(new FiltersActions.GetFiltersFailed(err)))
|
||||
);
|
||||
}
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Actions, Effect } from '@ngrx/effects';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/delay';
|
||||
import 'rxjs/add/operator/map';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||
import * as FoldersActions from '../actions/folders.actions';
|
||||
import { MailNgrxService } from '../../mail.service';
|
||||
|
||||
@Injectable()
|
||||
export class FoldersEffect
|
||||
{
|
||||
constructor(
|
||||
private actions: Actions,
|
||||
private mailService: MailNgrxService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Folders from Server
|
||||
* @type {Observable<any>}
|
||||
*/
|
||||
@Effect()
|
||||
getFolders: Observable<FoldersActions.FoldersActionsAll> =
|
||||
this.actions
|
||||
.ofType<FoldersActions.GetFolders>(FoldersActions.GET_FOLDERS)
|
||||
.pipe(
|
||||
switchMap((action) => {
|
||||
return this.mailService.getFolders()
|
||||
.pipe(
|
||||
map((folders: any) => {
|
||||
return new FoldersActions.GetFoldersSuccess(folders);
|
||||
}),
|
||||
catchError(err => of(new FoldersActions.GetFoldersFailed(err)))
|
||||
);
|
||||
}
|
||||
));
|
||||
}
|
||||
16
src/app/main/content/apps/mail-ngrx/store/effects/index.ts
Normal file
16
src/app/main/content/apps/mail-ngrx/store/effects/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { MailsEffect } from './mails.effects';
|
||||
import { FoldersEffect } from './folders.effects';
|
||||
import { FiltersEffect } from './filters.effects';
|
||||
import { LabelsEffect } from './labels.effects';
|
||||
|
||||
export const effects = [
|
||||
MailsEffect,
|
||||
FoldersEffect,
|
||||
FiltersEffect,
|
||||
LabelsEffect
|
||||
];
|
||||
|
||||
export * from './mails.effects';
|
||||
export * from './folders.effects';
|
||||
export * from './filters.effects';
|
||||
export * from './labels.effects';
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Actions, Effect } from '@ngrx/effects';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/delay';
|
||||
import 'rxjs/add/operator/map';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||
import * as LabelsActions from '../actions/labels.actions';
|
||||
import { MailNgrxService } from '../../mail.service';
|
||||
|
||||
@Injectable()
|
||||
export class LabelsEffect
|
||||
{
|
||||
constructor(
|
||||
private actions: Actions,
|
||||
private mailService: MailNgrxService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Labels from Server
|
||||
* @type {Observable<any>}
|
||||
*/
|
||||
@Effect()
|
||||
getLabels: Observable<LabelsActions.LabelsActionsAll> =
|
||||
this.actions
|
||||
.ofType<LabelsActions.GetLabels>(LabelsActions.GET_LABELS)
|
||||
.pipe(
|
||||
switchMap((action) => {
|
||||
return this.mailService.getLabels()
|
||||
.pipe(
|
||||
map((labels: any) => {
|
||||
return new LabelsActions.GetLabelsSuccess(labels);
|
||||
}),
|
||||
catchError(err => of(new LabelsActions.GetLabelsFailed(err)))
|
||||
);
|
||||
}
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
import { Actions, Effect } from '@ngrx/effects';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { map, mergeMap, exhaustMap, withLatestFrom } from 'rxjs/operators';
|
||||
import { getRouterState, State } from '../../../../../../store/reducers';
|
||||
import { getMailsState } from '../selectors';
|
||||
import * as MailsActions from '../actions/mails.actions';
|
||||
import * as fromRoot from '../../../../../../store';
|
||||
|
||||
import { MailNgrxService } from '../../mail.service';
|
||||
import { Mail } from '../../mail.model';
|
||||
|
||||
@Injectable()
|
||||
export class MailsEffect
|
||||
{
|
||||
routerState: any;
|
||||
|
||||
constructor(
|
||||
private actions: Actions,
|
||||
private mailService: MailNgrxService,
|
||||
private store: Store<State>
|
||||
)
|
||||
{
|
||||
this.store.select(getRouterState).subscribe(routerState => {
|
||||
if ( routerState )
|
||||
{
|
||||
this.routerState = routerState.state;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Mails with router parameters
|
||||
* @type {Observable<any>}
|
||||
*/
|
||||
@Effect()
|
||||
getMails: Observable<MailsActions.MailsActionsAll> =
|
||||
this.actions
|
||||
.ofType<MailsActions.GetMails>(MailsActions.GET_MAILS)
|
||||
.pipe(
|
||||
exhaustMap((action) => {
|
||||
|
||||
let handle = {
|
||||
id : '',
|
||||
value: ''
|
||||
};
|
||||
|
||||
const routeParams = Observable.of('labelHandle', 'filterHandle', 'folderHandle');
|
||||
routeParams.subscribe(param => {
|
||||
if ( this.routerState.params[param] )
|
||||
{
|
||||
handle = {
|
||||
id : param,
|
||||
value: this.routerState.params[param]
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return this.mailService.getMails(handle)
|
||||
.map((mails: Mail[]) => {
|
||||
|
||||
return new MailsActions.GetMailsSuccess({
|
||||
loaded: handle,
|
||||
mails : mails
|
||||
});
|
||||
})
|
||||
.catch(err => of(new MailsActions.GetMailsFailed(err)));
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Update Mail
|
||||
* @type {Observable<any>}
|
||||
*/
|
||||
@Effect()
|
||||
updateMail: Observable<MailsActions.MailsActionsAll> =
|
||||
this.actions
|
||||
.ofType<MailsActions.UpdateMail>(MailsActions.UPDATE_MAIL)
|
||||
.pipe(
|
||||
exhaustMap((action) => {
|
||||
return this.mailService.updateMail(action.payload)
|
||||
.map(() => {
|
||||
return new MailsActions.UpdateMailSuccess(action.payload);
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* UpdateMails
|
||||
* @type {Observable<any>}
|
||||
*/
|
||||
@Effect()
|
||||
updateMails: Observable<MailsActions.MailsActionsAll> =
|
||||
this.actions
|
||||
.ofType<MailsActions.UpdateMails>(MailsActions.UPDATE_MAILS)
|
||||
.pipe(
|
||||
exhaustMap((action) => {
|
||||
return Observable.forkJoin(
|
||||
action.payload.map(mail => this.mailService.updateMail(mail)),
|
||||
() => {
|
||||
return new MailsActions.UpdateMailsSuccess();
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Set Current Mail
|
||||
* @type {Observable<SetCurrentMailSuccess>}
|
||||
*/
|
||||
@Effect()
|
||||
setCurrentMail: Observable<Action> =
|
||||
this.actions
|
||||
.ofType<MailsActions.SetCurrentMail>(MailsActions.SET_CURRENT_MAIL)
|
||||
.pipe(
|
||||
withLatestFrom(this.store.select(getMailsState)),
|
||||
map(([action, state]) => {
|
||||
return new MailsActions.SetCurrentMailSuccess(state.entities[action.payload]);
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Check Current Mail
|
||||
* Navigate to parent directory if not exist in mail list
|
||||
* Update Current Mail if exist in mail list
|
||||
* @type {Observable<any>}
|
||||
*/
|
||||
@Effect()
|
||||
checkCurrentMail: Observable<Action> =
|
||||
this.actions
|
||||
.ofType<MailsActions.CheckCurrentMail>(MailsActions.CHECK_CURRENT_MAIL)
|
||||
.pipe(
|
||||
withLatestFrom(this.store.select(getMailsState)),
|
||||
map(([action, state]) => {
|
||||
|
||||
if ( !state.entities[this.routerState.params.mailId] )
|
||||
{
|
||||
return new fromRoot.Go({path: [this.routerState.url.replace(this.routerState.params.mailId, '')]});
|
||||
}
|
||||
return new MailsActions.SetCurrentMailSuccess(state.entities[this.routerState.params.mailId]);
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* On Get Mails Success
|
||||
* @type {Observable<CheckCurrentMail>}
|
||||
*/
|
||||
@Effect()
|
||||
getMailsSuccess: Observable<MailsActions.MailsActionsAll> =
|
||||
this.actions
|
||||
.ofType<MailsActions.GetMailsSuccess>(MailsActions.GET_MAILS_SUCCESS)
|
||||
.pipe(
|
||||
mergeMap(() =>
|
||||
[
|
||||
new MailsActions.CheckCurrentMail()
|
||||
])
|
||||
);
|
||||
/**
|
||||
* On Update Mails Success
|
||||
* @type {Observable<DeselectAllMails | GetMails>}
|
||||
*/
|
||||
@Effect()
|
||||
updateMailsSuccess: Observable<MailsActions.MailsActionsAll> =
|
||||
this.actions
|
||||
.ofType<MailsActions.UpdateMailsSuccess>(MailsActions.UPDATE_MAILS_SUCCESS)
|
||||
.pipe(
|
||||
mergeMap(() =>
|
||||
[
|
||||
new MailsActions.DeselectAllMails(),
|
||||
new MailsActions.GetMails()
|
||||
])
|
||||
);
|
||||
/**
|
||||
* On Update Mail Success
|
||||
* @type {Observable<GetMails>}
|
||||
*/
|
||||
@Effect()
|
||||
updateMailSuccess: Observable<MailsActions.MailsActionsAll> =
|
||||
this.actions
|
||||
.ofType<MailsActions.UpdateMailSuccess>(MailsActions.UPDATE_MAIL_SUCCESS)
|
||||
.debounceTime(500)
|
||||
.pipe(
|
||||
map(() => {
|
||||
return new MailsActions.GetMails();
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Set Folder on Selected Mails
|
||||
* @type {Observable<UpdateMails>}
|
||||
*/
|
||||
@Effect()
|
||||
setFolderOnSelectedMails: Observable<MailsActions.MailsActionsAll> =
|
||||
this.actions
|
||||
.ofType<MailsActions.SetFolderOnSelectedMails>(MailsActions.SET_FOLDER_ON_SELECTED_MAILS)
|
||||
.pipe(
|
||||
withLatestFrom(
|
||||
this.store.select(getMailsState)),
|
||||
map(([action, state]) => {
|
||||
const entities = {...state.entities};
|
||||
let mailsToUpdate = [];
|
||||
state.selectedMailIds
|
||||
.map(id => {
|
||||
mailsToUpdate = [
|
||||
...mailsToUpdate,
|
||||
entities[id] = {
|
||||
...entities[id],
|
||||
folder: action.payload
|
||||
}
|
||||
];
|
||||
});
|
||||
return new MailsActions.UpdateMails(mailsToUpdate);
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Add Label on Selected Mails
|
||||
* @type {Observable<UpdateMails>}
|
||||
*/
|
||||
@Effect()
|
||||
addLabelOnSelectedMails: Observable<MailsActions.MailsActionsAll> =
|
||||
this.actions
|
||||
.ofType<MailsActions.AddLabelOnSelectedMails>(MailsActions.ADD_LABEL_ON_SELECTED_MAILS)
|
||||
.pipe(
|
||||
withLatestFrom(this.store.select(getMailsState)),
|
||||
map(([action, state]) => {
|
||||
|
||||
const entities = {...state.entities};
|
||||
let mailsToUpdate = [];
|
||||
|
||||
state.selectedMailIds
|
||||
.map(id => {
|
||||
|
||||
let labels = [...entities[id].labels];
|
||||
|
||||
if ( !entities[id].labels.includes(action.payload) )
|
||||
{
|
||||
labels = [...labels, action.payload];
|
||||
}
|
||||
|
||||
mailsToUpdate = [
|
||||
...mailsToUpdate,
|
||||
entities[id] = {
|
||||
...entities[id],
|
||||
labels
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
return new MailsActions.UpdateMails(mailsToUpdate);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './resolve.guard';
|
||||
@@ -0,0 +1,134 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { map, switchMap, catchError, tap, take, filter } from 'rxjs/operators';
|
||||
import 'rxjs/add/observable/forkJoin';
|
||||
import { MailAppState } from '../reducers';
|
||||
import * as fromStore from '../index';
|
||||
import { getFiltersLoaded, getFoldersLoaded, getLabelsLoaded, getMailsLoaded } from '../selectors';
|
||||
import { RouterStateSnapshot } from '@angular/router/src/router_state';
|
||||
import { getRouterState } from '../../../../../../store/reducers';
|
||||
|
||||
@Injectable()
|
||||
export class ResolveGuard implements CanActivate
|
||||
{
|
||||
routerState: any;
|
||||
|
||||
constructor(
|
||||
private store: Store<MailAppState>
|
||||
)
|
||||
{
|
||||
this.store.select(getRouterState).subscribe(routerState => {
|
||||
if ( routerState )
|
||||
{
|
||||
this.routerState = routerState.state;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>
|
||||
{
|
||||
return this.checkStore().pipe(
|
||||
switchMap(() => of(true)),
|
||||
catchError(() => of(false))
|
||||
);
|
||||
}
|
||||
|
||||
checkStore(): Observable<any>
|
||||
{
|
||||
return Observable
|
||||
.forkJoin(
|
||||
this.getFolders(),
|
||||
this.getFilters(),
|
||||
this.getLabels()
|
||||
)
|
||||
.pipe(
|
||||
filter(([foldersLoaded, filtersLoaded, labelsLoaded]) => filtersLoaded && foldersLoaded && labelsLoaded),
|
||||
take(1),
|
||||
switchMap(() =>
|
||||
this.getMails()
|
||||
),
|
||||
take(1),
|
||||
map(() => this.store.dispatch(new fromStore.SetCurrentMail(this.routerState.params.mailId)))
|
||||
);
|
||||
}
|
||||
|
||||
getFolders()
|
||||
{
|
||||
return this.store.select(getFoldersLoaded)
|
||||
.pipe(
|
||||
tap(loaded => {
|
||||
if ( !loaded )
|
||||
{
|
||||
this.store.dispatch(new fromStore.GetFolders([]));
|
||||
}
|
||||
}),
|
||||
filter(loaded => loaded),
|
||||
take(1)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Filters
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
getFilters()
|
||||
{
|
||||
return this.store.select(getFiltersLoaded)
|
||||
.pipe(
|
||||
tap(loaded => {
|
||||
if ( !loaded )
|
||||
{
|
||||
this.store.dispatch(new fromStore.GetFilters([]));
|
||||
}
|
||||
}),
|
||||
filter(loaded => loaded),
|
||||
take(1)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Labels
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
getLabels()
|
||||
{
|
||||
return this.store.select(getLabelsLoaded)
|
||||
.pipe(
|
||||
tap(loaded => {
|
||||
if ( !loaded )
|
||||
{
|
||||
this.store.dispatch(new fromStore.GetLabels([]));
|
||||
}
|
||||
}),
|
||||
filter(loaded => loaded),
|
||||
take(1)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Mails
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
getMails()
|
||||
{
|
||||
return this.store.select(getMailsLoaded)
|
||||
.pipe(
|
||||
tap((loaded: any) => {
|
||||
|
||||
if ( !this.routerState.params[loaded.id] || this.routerState.params[loaded.id] !== loaded.value )
|
||||
{
|
||||
this.store.dispatch(new fromStore.GetMails());
|
||||
this.store.dispatch(new fromStore.SetSearchText(''));
|
||||
this.store.dispatch(new fromStore.DeselectAllMails());
|
||||
}
|
||||
}),
|
||||
filter((loaded: any) => {
|
||||
return this.routerState.params[loaded.id] && this.routerState.params[loaded.id] === loaded.value;
|
||||
}),
|
||||
take(1)
|
||||
);
|
||||
}
|
||||
}
|
||||
4
src/app/main/content/apps/mail-ngrx/store/index.ts
Normal file
4
src/app/main/content/apps/mail-ngrx/store/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './actions';
|
||||
export * from './reducers';
|
||||
export * from './selectors';
|
||||
export * from './effects';
|
||||
@@ -0,0 +1,53 @@
|
||||
import * as FiltersActions from '../actions/filters.actions';
|
||||
|
||||
export interface FiltersState
|
||||
{
|
||||
entities: { [id: number]: any };
|
||||
loading: boolean;
|
||||
loaded: boolean;
|
||||
}
|
||||
|
||||
export const FiltersInitialState: FiltersState = {
|
||||
entities: {},
|
||||
loading : false,
|
||||
loaded : false
|
||||
};
|
||||
|
||||
export function FiltersReducer(state = FiltersInitialState, action: FiltersActions.FiltersActionsAll): FiltersState
|
||||
{
|
||||
switch ( action.type )
|
||||
{
|
||||
case FiltersActions.GET_FILTERS:
|
||||
return {
|
||||
...state,
|
||||
loading: true,
|
||||
loaded : false
|
||||
};
|
||||
case FiltersActions.GET_FILTERS_SUCCESS:
|
||||
|
||||
const filters = action.payload;
|
||||
const entities = filters.reduce(
|
||||
(_entities: { [id: number]: any }, filter: any) => {
|
||||
return {
|
||||
..._entities,
|
||||
[filter.id]: filter
|
||||
};
|
||||
}, {});
|
||||
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
loaded : true,
|
||||
entities
|
||||
};
|
||||
|
||||
case FiltersActions.GET_FILTERS_FAILED:
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
loaded : false
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import * as FoldersActions from '../actions/folders.actions';
|
||||
|
||||
export interface FoldersState
|
||||
{
|
||||
entities: { [id: number]: any };
|
||||
loading: boolean;
|
||||
loaded: boolean;
|
||||
}
|
||||
|
||||
export const FoldersInitialState: FoldersState = {
|
||||
entities: {},
|
||||
loading : false,
|
||||
loaded : false
|
||||
};
|
||||
|
||||
export function FoldersReducer(state = FoldersInitialState, action: FoldersActions.FoldersActionsAll): FoldersState
|
||||
{
|
||||
switch ( action.type )
|
||||
{
|
||||
case FoldersActions.GET_FOLDERS:
|
||||
return {
|
||||
...state,
|
||||
loading: true,
|
||||
loaded : false
|
||||
};
|
||||
case FoldersActions.GET_FOLDERS_SUCCESS:
|
||||
|
||||
const folders = action.payload;
|
||||
const entities = folders.reduce(
|
||||
(_entities: { [id: number]: any }, folder: any) => {
|
||||
return {
|
||||
..._entities,
|
||||
[folder.id]: folder
|
||||
};
|
||||
}, {});
|
||||
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
loaded : true,
|
||||
entities
|
||||
};
|
||||
|
||||
case FoldersActions.GET_FOLDERS_FAILED:
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
loaded : false
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
34
src/app/main/content/apps/mail-ngrx/store/reducers/index.ts
Normal file
34
src/app/main/content/apps/mail-ngrx/store/reducers/index.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { ActionReducerMap, createFeatureSelector, createSelector } from '@ngrx/store';
|
||||
import { MailsReducer, MailsState } from './mails.reducer';
|
||||
import { FoldersReducer, FoldersState } from './folders.reducer';
|
||||
import { FiltersReducer, FiltersState } from './filters.reducer';
|
||||
import { LabelsReducer, LabelsState } from './labels.reducer';
|
||||
|
||||
export interface MailAppState
|
||||
{
|
||||
mails: MailsState;
|
||||
folders: FoldersState;
|
||||
filters: FiltersState;
|
||||
labels: LabelsState;
|
||||
}
|
||||
|
||||
export const getMailAppState = createFeatureSelector<MailAppState>(
|
||||
'mail-app'
|
||||
);
|
||||
|
||||
export const getAppState = createSelector(
|
||||
getMailAppState,
|
||||
(state: MailAppState) => state
|
||||
);
|
||||
|
||||
export const reducers: ActionReducerMap<MailAppState> = {
|
||||
mails : MailsReducer,
|
||||
folders: FoldersReducer,
|
||||
filters: FiltersReducer,
|
||||
labels : LabelsReducer
|
||||
};
|
||||
|
||||
export * from './mails.reducer';
|
||||
export * from './folders.reducer';
|
||||
export * from './filters.reducer';
|
||||
export * from './labels.reducer';
|
||||
@@ -0,0 +1,53 @@
|
||||
import * as LabelsActions from '../actions/labels.actions';
|
||||
|
||||
export interface LabelsState
|
||||
{
|
||||
entities: { [id: number]: any };
|
||||
loading: boolean;
|
||||
loaded: boolean;
|
||||
}
|
||||
|
||||
export const LabelsInitialState: LabelsState = {
|
||||
entities: {},
|
||||
loading : false,
|
||||
loaded : false
|
||||
};
|
||||
|
||||
export function LabelsReducer(state = LabelsInitialState, action: LabelsActions.LabelsActionsAll): LabelsState
|
||||
{
|
||||
switch ( action.type )
|
||||
{
|
||||
case LabelsActions.GET_LABELS:
|
||||
return {
|
||||
...state,
|
||||
loading: true,
|
||||
loaded : false
|
||||
};
|
||||
case LabelsActions.GET_LABELS_SUCCESS:
|
||||
|
||||
const labels = action.payload;
|
||||
const entities = labels.reduce(
|
||||
(_entities: { [id: number]: any }, label: any) => {
|
||||
return {
|
||||
..._entities,
|
||||
[label.id]: label
|
||||
};
|
||||
}, {});
|
||||
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
loaded : true,
|
||||
entities
|
||||
};
|
||||
|
||||
case LabelsActions.GET_LABELS_FAILED:
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
loaded : false
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import * as MailsActions from '../actions/mails.actions';
|
||||
import { Mail } from '../../mail.model';
|
||||
|
||||
export interface MailsState
|
||||
{
|
||||
entities: { [id: number]: Mail };
|
||||
currentMail: any;
|
||||
selectedMailIds: string[];
|
||||
searchText: string;
|
||||
loading: boolean;
|
||||
loaded: any;
|
||||
}
|
||||
|
||||
export const MailsInitialState: MailsState = {
|
||||
entities : {},
|
||||
currentMail : null,
|
||||
selectedMailIds: [],
|
||||
searchText : '',
|
||||
loading : false,
|
||||
loaded : false
|
||||
};
|
||||
|
||||
export function MailsReducer(state = MailsInitialState, action: MailsActions.MailsActionsAll): MailsState
|
||||
{
|
||||
switch ( action.type )
|
||||
{
|
||||
case MailsActions.GET_MAILS:
|
||||
{
|
||||
return {
|
||||
...state,
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
|
||||
case MailsActions.GET_MAILS_SUCCESS:
|
||||
{
|
||||
|
||||
const mails = action.payload.mails;
|
||||
const loaded = action.payload.loaded;
|
||||
const entities = mails.reduce(
|
||||
(_entities: { [id: number]: Mail }, mail: Mail) => {
|
||||
return {
|
||||
..._entities,
|
||||
[mail.id]: mail
|
||||
};
|
||||
}, {});
|
||||
|
||||
return {
|
||||
...state,
|
||||
entities,
|
||||
loading: false,
|
||||
loaded
|
||||
};
|
||||
}
|
||||
|
||||
case MailsActions.GET_MAILS_FAILED:
|
||||
{
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
loaded : false
|
||||
};
|
||||
}
|
||||
|
||||
case MailsActions.SET_CURRENT_MAIL_SUCCESS:
|
||||
{
|
||||
return {
|
||||
...state,
|
||||
currentMail: action.payload
|
||||
};
|
||||
}
|
||||
|
||||
case MailsActions.UPDATE_MAIL_SUCCESS:
|
||||
{
|
||||
return {
|
||||
...state,
|
||||
entities: {
|
||||
...state.entities,
|
||||
[action.payload.id]: action.payload
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case MailsActions.SET_SEARCH_TEXT:
|
||||
{
|
||||
|
||||
return {
|
||||
...state,
|
||||
searchText: action.payload
|
||||
};
|
||||
}
|
||||
|
||||
case MailsActions.TOGGLE_IN_SELECTED_MAILS:
|
||||
{
|
||||
|
||||
const mailId = action.payload;
|
||||
|
||||
let selectedMailIds = [...state.selectedMailIds];
|
||||
|
||||
if ( selectedMailIds.find(id => id === mailId) !== undefined )
|
||||
{
|
||||
selectedMailIds = selectedMailIds.filter(id => id !== mailId);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedMailIds = [...selectedMailIds, mailId];
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
selectedMailIds
|
||||
};
|
||||
}
|
||||
|
||||
case MailsActions.SELECT_ALL_MAILS:
|
||||
{
|
||||
const arr = Object.keys(state.entities).map(k => state.entities[k]);
|
||||
|
||||
const selectedMailIds = arr.map(mail => mail.id);
|
||||
|
||||
return {
|
||||
...state,
|
||||
selectedMailIds
|
||||
};
|
||||
}
|
||||
|
||||
case MailsActions.DESELECT_ALL_MAILS:
|
||||
{
|
||||
return {
|
||||
...state,
|
||||
selectedMailIds: []
|
||||
};
|
||||
}
|
||||
|
||||
case MailsActions.SELECT_MAILS_BY_PARAMETER:
|
||||
{
|
||||
const filter = action.payload;
|
||||
const arr = Object.keys(state.entities).map(k => state.entities[k]);
|
||||
const selectedMailIds = arr.filter(mail => mail[filter.parameter] === filter.value)
|
||||
.map(mail => mail.id);
|
||||
return {
|
||||
...state,
|
||||
selectedMailIds
|
||||
};
|
||||
}
|
||||
|
||||
case MailsActions.SET_FOLDER_ON_SELECTED_MAILS:
|
||||
{
|
||||
const entities = {...state.entities};
|
||||
|
||||
state.selectedMailIds.map(id => {
|
||||
entities[id] = {
|
||||
...entities[id],
|
||||
folder: action.payload
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
entities
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { createSelector } from '@ngrx/store';
|
||||
import { FiltersState, getMailAppState, MailAppState } from '../reducers';
|
||||
|
||||
export const getFiltersState = createSelector(
|
||||
getMailAppState,
|
||||
(state: MailAppState) => state.filters
|
||||
);
|
||||
|
||||
export const getFilters = createSelector(
|
||||
getFiltersState,
|
||||
(state: FiltersState) => state.entities
|
||||
);
|
||||
|
||||
export const getFiltersLoaded = createSelector(
|
||||
getFiltersState,
|
||||
(state: FiltersState) => state.loaded
|
||||
);
|
||||
|
||||
export const getFiltersArr = createSelector(
|
||||
getFilters,
|
||||
(entities) => Object.keys(entities).map((id) => entities[id])
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
import { createSelector } from '@ngrx/store';
|
||||
import { FoldersState, getMailAppState, MailAppState } from '../reducers';
|
||||
|
||||
export const getFoldersState = createSelector(
|
||||
getMailAppState,
|
||||
(state: MailAppState) => state.folders
|
||||
);
|
||||
|
||||
export const getFolders = createSelector(
|
||||
getFoldersState,
|
||||
(state: FoldersState) => state.entities
|
||||
);
|
||||
|
||||
export const getFoldersLoaded = createSelector(
|
||||
getFoldersState,
|
||||
(state: FoldersState) => state.loaded
|
||||
);
|
||||
|
||||
export const getFoldersArr = createSelector(
|
||||
getFolders,
|
||||
(entities) => Object.keys(entities).map((id) => entities[id])
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './mails.selectors';
|
||||
export * from './folders.selectors';
|
||||
export * from './filters.selectors';
|
||||
export * from './labels.selectors';
|
||||
@@ -0,0 +1,22 @@
|
||||
import { createSelector } from '@ngrx/store';
|
||||
import { LabelsState, getMailAppState, MailAppState } from '../reducers';
|
||||
|
||||
export const getLabelsState = createSelector(
|
||||
getMailAppState,
|
||||
(state: MailAppState) => state.labels
|
||||
);
|
||||
|
||||
export const getLabels = createSelector(
|
||||
getLabelsState,
|
||||
(state: LabelsState) => state.entities
|
||||
);
|
||||
|
||||
export const getLabelsLoaded = createSelector(
|
||||
getLabelsState,
|
||||
(state: LabelsState) => state.loaded
|
||||
);
|
||||
|
||||
export const getLabelsArr = createSelector(
|
||||
getLabels,
|
||||
(entities) => Object.keys(entities).map((id) => entities[id])
|
||||
);
|
||||
@@ -0,0 +1,42 @@
|
||||
import { createSelector } from '@ngrx/store';
|
||||
import { getMailAppState, MailAppState, MailsState } from '../reducers';
|
||||
import { FuseUtils } from '../../../../../../core/fuseUtils';
|
||||
|
||||
export const getMailsState = createSelector(
|
||||
getMailAppState,
|
||||
(state: MailAppState) => state.mails
|
||||
);
|
||||
|
||||
export const getMails = createSelector(
|
||||
getMailsState,
|
||||
(state: MailsState) => state.entities
|
||||
);
|
||||
|
||||
export const getMailsLoaded = createSelector(
|
||||
getMailsState,
|
||||
(state: MailsState) => state.loaded
|
||||
);
|
||||
|
||||
export const getSearchText = createSelector(
|
||||
getMailsState,
|
||||
(state: MailsState) => state.searchText
|
||||
);
|
||||
|
||||
export const getMailsArr = createSelector(
|
||||
getMails,
|
||||
getSearchText,
|
||||
(entities, searchText) => {
|
||||
const arr = Object.keys(entities).map((id) => entities[id]);
|
||||
return FuseUtils.filterArrayByString(arr, searchText);
|
||||
}
|
||||
);
|
||||
|
||||
export const getCurrentMail = createSelector(
|
||||
getMailsState,
|
||||
(state: MailsState) => state.currentMail
|
||||
);
|
||||
|
||||
export const getSelectedMailIds = createSelector(
|
||||
getMailsState,
|
||||
(state: MailsState) => state.selectedMailIds
|
||||
);
|
||||
16
src/app/main/content/apps/mail-ngrx/store/store.module.ts
Normal file
16
src/app/main/content/apps/mail-ngrx/store/store.module.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { reducers } from './reducers';
|
||||
import { effects } from './effects';
|
||||
|
||||
@NgModule({
|
||||
imports : [
|
||||
StoreModule.forFeature('mail-app', reducers),
|
||||
EffectsModule.forFeature(effects)
|
||||
],
|
||||
providers: []
|
||||
})
|
||||
export class MailAppStoreModule
|
||||
{
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export const locale = {
|
||||
'LABELS' : 'LABELS',
|
||||
'NO_MESSAGES' : 'There are no messages!',
|
||||
'SELECT_A_MESSAGE_TO_READ': 'Select a message to read',
|
||||
'SEARCH_PLACEHOLDER': 'Search for an e-mail or contact'
|
||||
'SEARCH_PLACEHOLDER' : 'Search for an e-mail or contact'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
||||
<div *ngIf="currentMail" fxHide.gt-lg>
|
||||
<div *ngIf="currentMail" fxHide.gt-xs>
|
||||
<button mat-icon-button (click)="deSelectCurrentMail()">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
|
||||
@@ -4,6 +4,8 @@ import { Subscription } from 'rxjs/Subscription';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { Mail } from './mail.model';
|
||||
import { FuseTranslationLoaderService } from '../../../../core/services/translation-loader.service';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import { locale as english } from './i18n/en';
|
||||
import { locale as turkish } from './i18n/tr';
|
||||
|
||||
@@ -80,12 +82,12 @@ export class FuseMailComponent implements OnInit, OnDestroy
|
||||
}
|
||||
});
|
||||
|
||||
/*this.searchInput.valueChanges
|
||||
this.searchInput.valueChanges
|
||||
.debounceTime(300)
|
||||
.distinctUntilChanged()
|
||||
.subscribe(searchText => {
|
||||
this.mailService.onSearchTextChanged.next(searchText);
|
||||
});*/
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Observable } from 'rxjs/Observable';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Mail } from './mail.model';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { FuseUtils } from 'app/core/fuseUtils';
|
||||
import { FuseUtils } from '../../../../core/fuseUtils';
|
||||
|
||||
@Injectable()
|
||||
export class MailService implements Resolve<any>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</button>
|
||||
|
||||
<form *ngIf="formActive" class="new-list-form" [formGroup]="form" (submit)="onFormSubmit()"
|
||||
fxFlex="1 0 auto" fxFlex="row" fxLayoutAlign="start center">
|
||||
fxLayout="row" fxLayoutAlign="start center" fxFlex="1 0 auto">
|
||||
|
||||
<input formControlName="name" #nameInput fxFlex placeholder="Write a list Name">
|
||||
|
||||
|
||||
@@ -131,8 +131,8 @@
|
||||
|
||||
<div *ngIf="card" mat-dialog-content class="p-24 m-0" fusePerfectScrollbar>
|
||||
|
||||
<div fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="space-between center"
|
||||
fxLayout.xs="column" fxLayoutAlign="center center">
|
||||
<div fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="space-between center"
|
||||
fxLayout="column" fxLayoutAlign="center center">
|
||||
|
||||
<!-- BREADCRUMB -->
|
||||
<div class="card-breadcrumb mb-16 mb-sm-0" fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
@@ -2,9 +2,16 @@
|
||||
|
||||
.scrumboard-card-dialog {
|
||||
|
||||
@include media-breakpoint('xs') {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up('xs') {
|
||||
width: 720px;
|
||||
}
|
||||
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
width: 720px;
|
||||
|
||||
.mat-toolbar {
|
||||
|
||||
@@ -162,9 +169,16 @@
|
||||
background-size: contain;
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
margin-right: 24px;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
|
||||
@include media-breakpoint('xs') {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up('xs') {
|
||||
margin-right: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-content {
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
|
||||
|
||||
<form [formGroup]="form" (ngSubmit)="onFormSubmit()"
|
||||
class="board-name-form" fxFlex="1 0 auto"
|
||||
*ngIf="formActive" fxFlex="row">
|
||||
class="board-name-form" fxLayout="row" fxFlex="1 0 auto" *ngIf="formActive">
|
||||
|
||||
<input formControlName="name" #nameInput fxFlex="1 0 auto" placeholder="Write a board name">
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
</div>
|
||||
|
||||
<form [formGroup]="form" (ngSubmit)="onFormSubmit()"
|
||||
class="list-header-name-form" fxFlex="1 0 auto"
|
||||
*ngIf="formActive" fxFlex="row">
|
||||
class="list-header-name-form" fxLayout="row" fxFlex="1 0 auto"
|
||||
*ngIf="formActive">
|
||||
|
||||
<input formControlName="name" #nameInput fxFlex placeholder="Write a list Name">
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Component, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { FuseUtils } from '../../../../../../core/fuseUtils';
|
||||
import { ScrumboardService } from 'app/main/content/apps/scrumboard/scrumboard.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { MatDialog, MatDialogRef } from '@angular/material';
|
||||
import { FuseScrumboardCardDialogComponent } from '../dialogs/card/card.component';
|
||||
import { FuseConfirmDialogComponent } from '../../../../../../core/components/confirm-dialog/confirm-dialog.component';
|
||||
import { ScrumboardService } from '../../scrumboard.service';
|
||||
import { Card } from '../../card.model';
|
||||
import { FusePerfectScrollbarDirective } from '../../../../../../core/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.directive';
|
||||
|
||||
@@ -24,7 +23,6 @@ export class FuseScrumboardBoardListComponent implements OnInit, OnDestroy
|
||||
@ViewChild(FusePerfectScrollbarDirective) listScroll: FusePerfectScrollbarDirective;
|
||||
|
||||
onBoardChanged: Subscription;
|
||||
|
||||
confirmDialogRef: MatDialogRef<FuseConfirmDialogComponent>;
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<p>
|
||||
<code>fuse-navigation</code> is a custom built Fuse component allows you to create a multi-level collapsable
|
||||
navigation.
|
||||
navigation. It has built-in support for translations using <b>ngx-translate</b> module.
|
||||
</p>
|
||||
|
||||
<div class="my-48">
|
||||
@@ -40,6 +40,11 @@
|
||||
<b>Collapsable</b> and <b>Item</b>. These items can be mixed and matched to create unique and complex
|
||||
navigation layouts.
|
||||
</p>
|
||||
<p class="py-8">
|
||||
Navigation model can be found in <code>src/app/navigation</code> folder along with its translation
|
||||
files. Navigation model and its translation files are set in <b>app.component.ts</b> file. Check that
|
||||
file for more detailed usage example.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="my-48">
|
||||
@@ -48,9 +53,10 @@
|
||||
<fuse-hljs lang="json" class="source-code">
|
||||
<textarea #source hidden="hidden">
|
||||
{
|
||||
'title': 'COMPONENTS',
|
||||
'type' : 'group',
|
||||
'children': [
|
||||
'title' : 'COMPONENTS',
|
||||
'translate': 'NAV.COMPONENTS',
|
||||
'type' : 'group',
|
||||
'children' : [
|
||||
{
|
||||
'title': 'ngx-datatable',
|
||||
'type' : 'item',
|
||||
@@ -69,10 +75,11 @@
|
||||
<fuse-hljs lang="json" class="source-code">
|
||||
<textarea #source hidden="hidden">
|
||||
{
|
||||
'title' : 'Datatables',
|
||||
'type' : 'collapse',
|
||||
'icon' : 'border_all',
|
||||
'children': [
|
||||
'title' : 'Datatables',
|
||||
'translate': 'NAV.DATATABLES',
|
||||
'type' : 'collapse',
|
||||
'icon' : 'border_all',
|
||||
'children' : [
|
||||
{
|
||||
'title': 'ngx-datatable',
|
||||
'type' : 'nav-item',
|
||||
@@ -91,10 +98,11 @@
|
||||
<fuse-hljs lang="json" class="source-code">
|
||||
<textarea #source hidden="hidden">
|
||||
{
|
||||
'title': 'Countdown',
|
||||
'type' : 'item',
|
||||
'icon' : 'settings_input_component',
|
||||
'url' : '/components/countdown'
|
||||
'title' : 'Countdown',
|
||||
'translate': 'NAV.COUNTDOWN',
|
||||
'type' : 'item',
|
||||
'icon' : 'settings_input_component',
|
||||
'url' : '/components/countdown'
|
||||
},
|
||||
</textarea>
|
||||
</fuse-hljs>
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div id="forgot-password-form-wrapper" fusePerfectScrollbar *fuseIfOnDom [@animate]="{value:'*',params:{delay:'300ms',x:'100%'}}">
|
||||
<div id="forgot-password-form-wrapper" fusePerfectScrollbar *fuseIfOnDom
|
||||
[@animate]="{value:'*',params:{delay:'300ms',x:'100%'}}">
|
||||
|
||||
<div id="forgot-password-form">
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#forgot-password {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background: url('/assets/images/backgrounds/march.jpg') no-repeat;
|
||||
background: url('/assets/images/backgrounds/dark-material-bg.jpg') no-repeat;
|
||||
background-size: cover;
|
||||
|
||||
#forgot-password-intro {
|
||||
@@ -39,7 +39,7 @@
|
||||
min-width: 400px;
|
||||
max-width: 400px;
|
||||
background: #FFFFFF;
|
||||
@include mat-elevation(7);
|
||||
@include mat-elevation(16);
|
||||
|
||||
@include media-breakpoint('sm') {
|
||||
width: 360px;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { FuseConfigService } from '../../../../../core/services/config.service';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
@@ -57,7 +56,7 @@ export class FuseForgotPassword2Component implements OnInit
|
||||
this.forgotPasswordFormErrors[field] = {};
|
||||
|
||||
// Get the control
|
||||
const control = this.forgotPasswordFormErrors.get(field);
|
||||
const control = this.forgotPasswordForm.get(field);
|
||||
|
||||
if ( control && control.dirty && !control.valid )
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div id="forgot-password" fxLayout="column" fusePerfectScrollbar>
|
||||
|
||||
<div id="forgot-password-form-wrapper" fxLayout="column" fxLayoutAlign="center center" *fuseIfOnDom [@animate]="{value:'*',params:{duration:'300ms',y:'100px'}}">
|
||||
<div id="forgot-password-form-wrapper" fxLayout="column" fxLayoutAlign="center center" *fuseIfOnDom
|
||||
[@animate]="{value:'*',params:{duration:'300ms',y:'100px'}}">
|
||||
|
||||
<div id="forgot-password-form">
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
@import "../../../../../core/scss/fuse";
|
||||
@import "src/app/core/scss/fuse";
|
||||
|
||||
:host {
|
||||
|
||||
#forgot-password {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
background: url('/assets/images/backgrounds/march.jpg') no-repeat;
|
||||
background: url('/assets/images/backgrounds/dark-material-bg.jpg') no-repeat;
|
||||
background-size: cover;
|
||||
|
||||
#forgot-password-form-wrapper {
|
||||
@@ -22,7 +22,7 @@
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
background: #FFFFFF;
|
||||
@include mat-elevation(7);
|
||||
@include mat-elevation(16);
|
||||
|
||||
@include media-breakpoint('xs') {
|
||||
padding: 24px;
|
||||
|
||||
@@ -56,7 +56,7 @@ export class FuseForgotPasswordComponent implements OnInit
|
||||
this.forgotPasswordFormErrors[field] = {};
|
||||
|
||||
// Get the control
|
||||
const control = this.forgotPasswordFormErrors.get(field);
|
||||
const control = this.forgotPasswordForm.get(field);
|
||||
|
||||
if ( control && control.dirty && !control.valid )
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#lock {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
background: url('/assets/images/backgrounds/march.jpg') no-repeat;
|
||||
background: url('/assets/images/backgrounds/dark-material-bg.jpg') no-repeat;
|
||||
background-size: cover;
|
||||
|
||||
#lock-form-wrapper {
|
||||
@@ -23,7 +23,7 @@
|
||||
max-width: 420px;
|
||||
padding: 48px 32px 32px 32px;
|
||||
background: #FFFFFF;
|
||||
@include mat-elevation(7);
|
||||
@include mat-elevation(16);
|
||||
|
||||
@include media-breakpoint('xs') {
|
||||
padding: 24px;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { FuseConfigService } from '../../../../../core/services/config.service';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
@@ -55,7 +54,7 @@ export class FuseLockComponent implements OnInit
|
||||
{
|
||||
for ( const field in this.lockFormErrors )
|
||||
{
|
||||
if ( this.lockFormErrors.hasOwnProperty(field) )
|
||||
if ( !this.lockFormErrors.hasOwnProperty(field) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div id="login-form-wrapper" fusePerfectScrollbar *fuseIfOnDom [@animate]="{value:'*',params:{delay:'300ms',x:'100%'}}">
|
||||
<div id="login-form-wrapper" fusePerfectScrollbar *fuseIfOnDom
|
||||
[@animate]="{value:'*',params:{delay:'300ms',x:'100%'}}">
|
||||
|
||||
<div id="login-form">
|
||||
|
||||
@@ -41,7 +42,7 @@
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Password" formControlName="password">
|
||||
<input matInput type="password" placeholder="Password" formControlName="password">
|
||||
<mat-error *ngIf="loginFormErrors.password.required">
|
||||
Password is required
|
||||
</mat-error>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#login {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background: url('/assets/images/backgrounds/march.jpg') no-repeat;
|
||||
background: url('/assets/images/backgrounds/dark-material-bg.jpg') no-repeat;
|
||||
background-size: cover;
|
||||
|
||||
#login-intro {
|
||||
@@ -39,7 +39,7 @@
|
||||
min-width: 400px;
|
||||
max-width: 400px;
|
||||
background: #FFFFFF;
|
||||
@include mat-elevation(7);
|
||||
@include mat-elevation(16);
|
||||
|
||||
@include media-breakpoint('sm') {
|
||||
width: 360px;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { FuseConfigService } from '../../../../../core/services/config.service';
|
||||
import { fuseAnimations } from '../../../../../core/animations';
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Password" formControlName="password">
|
||||
<input matInput type="password" placeholder="Password" formControlName="password">
|
||||
<mat-error *ngIf="loginFormErrors.password.required">
|
||||
Password is required
|
||||
</mat-error>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user