Improving the codebase (wip)

This commit is contained in:
Sercan Yemen 2018-05-21 15:42:34 +03:00
parent 0039f44936
commit 742da904da
124 changed files with 3174 additions and 1491 deletions

View File

@ -3,6 +3,7 @@ import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { MatMomentDateModule } from '@angular/material-moment-adapter';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import 'hammerjs'; import 'hammerjs';
@ -64,6 +65,9 @@ const appRoutes: Routes = [
passThruUnknownUrl: true passThruUnknownUrl: true
}), }),
// Material moment date module
MatMomentDateModule,
// Fuse modules // Fuse modules
FuseModule.forRoot(fuseConfig), FuseModule.forRoot(fuseConfig),
FuseSharedModule, FuseSharedModule,

View File

@ -6,7 +6,7 @@ import { BehaviorSubject, Observable } from 'rxjs';
@Injectable() @Injectable()
export class AcademyCourseService implements Resolve<any> export class AcademyCourseService implements Resolve<any>
{ {
onCourseChanged: BehaviorSubject<any> = new BehaviorSubject({}); onCourseChanged: BehaviorSubject<any>;
/** /**
* Constructor * Constructor
@ -17,6 +17,8 @@ export class AcademyCourseService implements Resolve<any>
private _httpClient: HttpClient private _httpClient: HttpClient
) )
{ {
// Set the defaults
this.onCourseChanged = new BehaviorSubject({});
} }
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------

View File

@ -6,8 +6,8 @@ import { BehaviorSubject, Observable } from 'rxjs';
@Injectable() @Injectable()
export class AcademyCoursesService implements Resolve<any> export class AcademyCoursesService implements Resolve<any>
{ {
onCategoriesChanged: BehaviorSubject<any> = new BehaviorSubject({}); onCategoriesChanged: BehaviorSubject<any>;
onCoursesChanged: BehaviorSubject<any> = new BehaviorSubject({}); onCoursesChanged: BehaviorSubject<any>;
/** /**
* Constructor * Constructor
@ -18,6 +18,9 @@ export class AcademyCoursesService implements Resolve<any>
private _httpClient: HttpClient private _httpClient: HttpClient
) )
{ {
// Set the defaults
this.onCategoriesChanged = new BehaviorSubject({});
this.onCoursesChanged = new BehaviorSubject({});
} }
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------

View File

@ -14,7 +14,7 @@ const routes = [
}, },
{ {
path : 'mail', path : 'mail',
loadChildren: './mail/mail.module#FuseMailModule' loadChildren: './mail/mail.module#MailModule'
}, },
{ {
path : 'mail-ngrx', path : 'mail-ngrx',

View File

@ -1,6 +1,6 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { MatButtonModule, MatDatepickerModule, MatFormFieldModule, MatIconModule, MatInputModule, MatSlideToggleModule, MatToolbarModule } from '@angular/material'; import { MatButtonModule, MatDatepickerModule, MatDialogModule, MatFormFieldModule, MatIconModule, MatInputModule, MatSlideToggleModule, MatToolbarModule } from '@angular/material';
import { ColorPickerModule } from 'ngx-color-picker'; import { ColorPickerModule } from 'ngx-color-picker';
import { CalendarModule as AngularCalendarModule } from 'angular-calendar'; import { CalendarModule as AngularCalendarModule } from 'angular-calendar';
@ -32,6 +32,7 @@ const routes: Routes = [
MatButtonModule, MatButtonModule,
MatDatepickerModule, MatDatepickerModule,
MatDialogModule,
MatFormFieldModule, MatFormFieldModule,
MatIconModule, MatIconModule,
MatInputModule, MatInputModule,

View File

@ -7,7 +7,7 @@ import { Observable, Subject } from 'rxjs';
export class CalendarService implements Resolve<any> export class CalendarService implements Resolve<any>
{ {
events: any; events: any;
onEventsUpdated = new Subject<any>(); onEventsUpdated: Subject<any>;
/** /**
* Constructor * Constructor
@ -18,7 +18,8 @@ export class CalendarService implements Resolve<any>
private _httpClient: HttpClient private _httpClient: HttpClient
) )
{ {
// Set the defaults
this.onEventsUpdated = new Subject();
} }
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
<mat-toolbar class="mat-accent m-0"> <mat-toolbar class="mat-accent m-0">
<mat-toolbar-row fxFlex fxLayout="row" fxLayoutAlign="space-between center"> <mat-toolbar-row fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<span class="title dialog-title">{{dialogTitle}}</span> <span class="title dialog-title">{{dialogTitle}}</span>
<button mat-icon-button (click)="dialogRef.close()" aria-label="Close dialog"> <button mat-icon-button (click)="_matDialogRef.close()" aria-label="Close dialog">
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
</mat-toolbar-row> </mat-toolbar-row>
@ -117,7 +117,7 @@
<button *ngIf="action !=='edit'" <button *ngIf="action !=='edit'"
mat-raised-button mat-raised-button
(click)="dialogRef.close(eventForm)" (click)="_matDialogRef.close(eventForm)"
class="save-button mat-accent" class="save-button mat-accent"
[disabled]="eventForm.invalid" [disabled]="eventForm.invalid"
aria-label="SAVE"> aria-label="SAVE">
@ -126,7 +126,7 @@
<button *ngIf="action ==='edit'" <button *ngIf="action ==='edit'"
mat-raised-button mat-raised-button
(click)="dialogRef.close(['save',eventForm])" (click)="_matDialogRef.close(['save',eventForm])"
class="save-button mat-accent" class="save-button mat-accent"
[disabled]="eventForm.invalid" [disabled]="eventForm.invalid"
aria-label="SAVE"> aria-label="SAVE">
@ -135,7 +135,7 @@
<button *ngIf="action ==='edit'" <button *ngIf="action ==='edit'"
mat-icon-button mat-icon-button
(click)="dialogRef.close(['delete',eventForm])" (click)="_matDialogRef.close(['delete',eventForm])"
aria-label="Delete" aria-label="Delete"
matTooltip="Delete"> matTooltip="Delete">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>

View File

@ -8,7 +8,7 @@ import { MatColors } from '@fuse/mat-colors';
import { CalendarEventModel } from 'app/main/apps/calendar/event.model'; import { CalendarEventModel } from 'app/main/apps/calendar/event.model';
@Component({ @Component({
selector : 'fuse-calendar-event-form-dialog', selector : 'calendar-event-form-dialog',
templateUrl : './event-form.component.html', templateUrl : './event-form.component.html',
styleUrls : ['./event-form.component.scss'], styleUrls : ['./event-form.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
@ -25,12 +25,12 @@ export class CalendarEventFormDialogComponent
/** /**
* Constructor * Constructor
* *
* @param {MatDialogRef<CalendarEventFormDialogComponent>} dialogRef * @param {MatDialogRef<CalendarEventFormDialogComponent>} _matDialogRef
* @param _data * @param _data
* @param {FormBuilder} _formBuilder * @param {FormBuilder} _formBuilder
*/ */
constructor( constructor(
public dialogRef: MatDialogRef<CalendarEventFormDialogComponent>, private _matDialogRef: MatDialogRef<CalendarEventFormDialogComponent>,
@Inject(MAT_DIALOG_DATA) private _data: any, @Inject(MAT_DIALOG_DATA) private _data: any,
private _formBuilder: FormBuilder private _formBuilder: FormBuilder
) )

View File

@ -11,23 +11,64 @@ export class ChatService implements Resolve<any>
contacts: any[]; contacts: any[];
chats: any[]; chats: any[];
user: any; user: any;
onChatSelected = new BehaviorSubject<any>(null); onChatSelected: BehaviorSubject<any>;
onContactSelected = new BehaviorSubject<any>(null); onContactSelected: BehaviorSubject<any>;
onChatsUpdated = new Subject<any>(); onChatsUpdated: Subject<any>;
onUserUpdated = new Subject<any>(); onUserUpdated: Subject<any>;
onLeftSidenavViewChanged = new Subject<any>(); onLeftSidenavViewChanged: Subject<any>;
onRightSidenavViewChanged = new Subject<any>(); onRightSidenavViewChanged: Subject<any>;
constructor(private http: HttpClient) /**
* Constructor
*
* @param {HttpClient} _httpClient
*/
constructor(
private _httpClient: HttpClient
)
{ {
// Set the defaults
this.onChatSelected = new BehaviorSubject(null);
this.onContactSelected = new BehaviorSubject(null);
this.onChatsUpdated = new Subject();
this.onUserUpdated = new Subject();
this.onLeftSidenavViewChanged = new Subject();
this.onRightSidenavViewChanged = new Subject();
}
/**
* Resolver
*
* @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any}
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
return new Promise((resolve, reject) => {
Promise.all([
this.getContacts(),
this.getChats(),
this.getUser()
]).then(
([contacts, chats, user]) => {
this.contacts = contacts;
this.chats = chats;
this.user = user;
resolve();
},
reject
);
});
} }
/** /**
* Get chat * Get chat
*
* @param contactId * @param contactId
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
getChat(contactId) getChat(contactId): Promise<any>
{ {
const chatItem = this.user.chatList.find((item) => { const chatItem = this.user.chatList.find((item) => {
return item.contactId === contactId; return item.contactId === contactId;
@ -45,7 +86,7 @@ export class ChatService implements Resolve<any>
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/chat-chats/' + chatItem.id) this._httpClient.get('api/chat-chats/' + chatItem.id)
.subscribe((response: any) => { .subscribe((response: any) => {
const chat = response; const chat = response;
@ -68,11 +109,12 @@ export class ChatService implements Resolve<any>
} }
/** /**
* Create New Chat * Create new chat
*
* @param contactId * @param contactId
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
createNewChat(contactId) createNewChat(contactId): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -103,13 +145,13 @@ export class ChatService implements Resolve<any>
/** /**
* Post the created chat * Post the created chat
*/ */
this.http.post('api/chat-chats', {...chat}) this._httpClient.post('api/chat-chats', {...chat})
.subscribe((response: any) => { .subscribe((response: any) => {
/** /**
* Post the new the user data * Post the new the user data
*/ */
this.http.post('api/chat-user/' + this.user.id, this.user) this._httpClient.post('api/chat-user/' + this.user.id, this.user)
.subscribe(newUserData => { .subscribe(newUserData => {
/** /**
@ -125,30 +167,33 @@ export class ChatService implements Resolve<any>
} }
/** /**
* Select Contact * Select contact
*
* @param contact * @param contact
*/ */
selectContact(contact) selectContact(contact): void
{ {
this.onContactSelected.next(contact); this.onContactSelected.next(contact);
} }
/** /**
* Set user status * Set user status
*
* @param status * @param status
*/ */
setUserStatus(status) setUserStatus(status): void
{ {
this.user.status = status; this.user.status = status;
} }
/** /**
* Update user data * Update user data
*
* @param userData * @param userData
*/ */
updateUserData(userData) updateUserData(userData): void
{ {
this.http.post('api/chat-user/' + this.user.id, userData) this._httpClient.post('api/chat-user/' + this.user.id, userData)
.subscribe((response: any) => { .subscribe((response: any) => {
this.user = userData; this.user = userData;
} }
@ -157,6 +202,7 @@ export class ChatService implements Resolve<any>
/** /**
* Update the chat dialog * Update the chat dialog
*
* @param chatId * @param chatId
* @param dialog * @param dialog
* @returns {Promise<any>} * @returns {Promise<any>}
@ -170,7 +216,7 @@ export class ChatService implements Resolve<any>
dialog: dialog dialog: dialog
}; };
this.http.post('api/chat-chats/' + chatId, newData) this._httpClient.post('api/chat-chats/' + chatId, newData)
.subscribe(updatedChat => { .subscribe(updatedChat => {
resolve(updatedChat); resolve(updatedChat);
}, reject); }, reject);
@ -178,38 +224,14 @@ export class ChatService implements Resolve<any>
} }
/** /**
* The Chat App Main Resolver * Get contacts
* @param {ActivatedRouteSnapshot} route *
* @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any}
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
return new Promise((resolve, reject) => {
Promise.all([
this.getContacts(),
this.getChats(),
this.getUser()
]).then(
([contacts, chats, user]) => {
this.contacts = contacts;
this.chats = chats;
this.user = user;
resolve();
},
reject
);
});
}
/**
* Get Contacts
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
getContacts(): Promise<any> getContacts(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/chat-contacts') this._httpClient.get('api/chat-contacts')
.subscribe((response: any) => { .subscribe((response: any) => {
resolve(response); resolve(response);
}, reject); }, reject);
@ -217,13 +239,14 @@ export class ChatService implements Resolve<any>
} }
/** /**
* Get Chats * Get chats
*
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
getChats(): Promise<any> getChats(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/chat-chats') this._httpClient.get('api/chat-chats')
.subscribe((response: any) => { .subscribe((response: any) => {
resolve(response); resolve(response);
}, reject); }, reject);
@ -231,13 +254,14 @@ export class ChatService implements Resolve<any>
} }
/** /**
* Get User * Get user
*
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
getUser(): Promise<any> getUser(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/chat-user') this._httpClient.get('api/chat-user')
.subscribe((response: any) => { .subscribe((response: any) => {
resolve(response[0]); resolve(response[0]);
}, reject); }, reject);

View File

@ -2,7 +2,7 @@
<mat-toolbar matDialogTitle class="mat-accent m-0"> <mat-toolbar matDialogTitle class="mat-accent m-0">
<mat-toolbar-row fxLayout="row" fxLayoutAlign="space-between center"> <mat-toolbar-row fxLayout="row" fxLayoutAlign="space-between center">
<span class="title dialog-title">{{dialogTitle}}</span> <span class="title dialog-title">{{dialogTitle}}</span>
<button mat-icon-button (click)="dialogRef.close()" aria-label="Close dialog"> <button mat-icon-button (click)="_matDialogRef.close()" aria-label="Close dialog">
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
</mat-toolbar-row> </mat-toolbar-row>
@ -98,7 +98,7 @@
<button *ngIf="action !=='edit'" <button *ngIf="action !=='edit'"
mat-raised-button mat-raised-button
(click)="dialogRef.close(contactForm)" (click)="_matDialogRef.close(contactForm)"
class="save-button mat-accent" class="save-button mat-accent"
[disabled]="contactForm.invalid" [disabled]="contactForm.invalid"
aria-label="SAVE"> aria-label="SAVE">
@ -107,7 +107,7 @@
<button *ngIf="action ==='edit'" <button *ngIf="action ==='edit'"
mat-raised-button mat-raised-button
(click)="dialogRef.close(['save',contactForm])" (click)="_matDialogRef.close(['save',contactForm])"
class="save-button mat-accent" class="save-button mat-accent"
[disabled]="contactForm.invalid" [disabled]="contactForm.invalid"
aria-label="SAVE"> aria-label="SAVE">
@ -116,7 +116,7 @@
<button *ngIf="action ==='edit'" <button *ngIf="action ==='edit'"
mat-icon-button mat-icon-button
(click)="dialogRef.close(['delete',contactForm])" (click)="_matDialogRef.close(['delete',contactForm])"
aria-label="Delete" aria-label="Delete"
matTooltip="Delete"> matTooltip="Delete">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>

View File

@ -21,16 +21,17 @@ export class ContactsContactFormDialogComponent
/** /**
* Constructor * Constructor
* *
* @param {MatDialogRef<ContactsContactFormDialogComponent>} _dialogRef * @param {MatDialogRef<ContactsContactFormDialogComponent>} _matDialogRef
* @param _data * @param _data
* @param {FormBuilder} _formBuilder * @param {FormBuilder} _formBuilder
*/ */
constructor( constructor(
private _dialogRef: MatDialogRef<ContactsContactFormDialogComponent>, private _matDialogRef: MatDialogRef<ContactsContactFormDialogComponent>,
@Inject(MAT_DIALOG_DATA) private _data: any, @Inject(MAT_DIALOG_DATA) private _data: any,
private _formBuilder: FormBuilder private _formBuilder: FormBuilder
) )
{ {
// Set the defaults
this.action = _data.action; this.action = _data.action;
if ( this.action === 'edit' ) if ( this.action === 'edit' )

View File

@ -33,7 +33,7 @@ export class ContactsComponent implements OnInit, OnDestroy
*/ */
constructor( constructor(
private _contactsService: ContactsService, private _contactsService: ContactsService,
public _matDialog: MatDialog private _matDialog: MatDialog
) )
{ {
// Set the defaults // Set the defaults

View File

@ -10,11 +10,11 @@ import { Contact } from 'app/main/apps/contacts/contact.model';
@Injectable() @Injectable()
export class ContactsService implements Resolve<any> export class ContactsService implements Resolve<any>
{ {
onContactsChanged: BehaviorSubject<any> = new BehaviorSubject([]); onContactsChanged: BehaviorSubject<any>;
onSelectedContactsChanged: BehaviorSubject<any> = new BehaviorSubject([]); onSelectedContactsChanged: BehaviorSubject<any>;
onUserDataChanged: BehaviorSubject<any> = new BehaviorSubject([]); onUserDataChanged: BehaviorSubject<any>;
onSearchTextChanged: Subject<any> = new Subject(); onSearchTextChanged: Subject<any>;
onFilterChanged: Subject<any> = new Subject(); onFilterChanged: Subject<any>;
contacts: Contact[]; contacts: Contact[];
user: any; user: any;
@ -32,6 +32,12 @@ export class ContactsService implements Resolve<any>
private _httpClient: HttpClient private _httpClient: HttpClient
) )
{ {
// Set the defaults
this.onContactsChanged = new BehaviorSubject([]);
this.onSelectedContactsChanged = new BehaviorSubject([]);
this.onUserDataChanged = new BehaviorSubject([]);
this.onSearchTextChanged = new Subject();
this.onFilterChanged = new Subject();
} }
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------

View File

@ -10,7 +10,7 @@ import { Order } from 'app/main/apps/e-commerce/order/order.model';
import { EcommerceOrderService } from 'app/main/apps/e-commerce/order/order.service'; import { EcommerceOrderService } from 'app/main/apps/e-commerce/order/order.service';
@Component({ @Component({
selector : 'fuse-e-commerce-order', selector : 'e-commerce-order',
templateUrl : './order.component.html', templateUrl : './order.component.html',
styleUrls : ['./order.component.scss'], styleUrls : ['./order.component.scss'],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,

View File

@ -8,7 +8,7 @@ export class EcommerceOrderService implements Resolve<any>
{ {
routeParams: any; routeParams: any;
order: any; order: any;
onOrderChanged: BehaviorSubject<any> = new BehaviorSubject({}); onOrderChanged: BehaviorSubject<any>;
/** /**
* Constructor * Constructor
@ -19,6 +19,8 @@ export class EcommerceOrderService implements Resolve<any>
private _httpClient: HttpClient private _httpClient: HttpClient
) )
{ {
// Set the defaults
this.onOrderChanged = new BehaviorSubject({});
} }
/** /**

View File

@ -7,7 +7,7 @@ import { BehaviorSubject, Observable } from 'rxjs';
export class EcommerceOrdersService implements Resolve<any> export class EcommerceOrdersService implements Resolve<any>
{ {
orders: any[]; orders: any[];
onOrdersChanged: BehaviorSubject<any> = new BehaviorSubject({}); onOrdersChanged: BehaviorSubject<any>;
/** /**
* Constructor * Constructor
@ -18,6 +18,8 @@ export class EcommerceOrdersService implements Resolve<any>
private _httpClient: HttpClient private _httpClient: HttpClient
) )
{ {
// Set the defaults
this.onOrdersChanged = new BehaviorSubject({});
} }
/** /**

View File

@ -8,7 +8,7 @@ export class EcommerceProductService implements Resolve<any>
{ {
routeParams: any; routeParams: any;
product: any; product: any;
onProductChanged: BehaviorSubject<any> = new BehaviorSubject({}); onProductChanged: BehaviorSubject<any>;
/** /**
* Constructor * Constructor
@ -19,6 +19,8 @@ export class EcommerceProductService implements Resolve<any>
private _httpClient: HttpClient private _httpClient: HttpClient
) )
{ {
// Set the defaults
this.onProductChanged = new BehaviorSubject({});
} }
/** /**

View File

@ -7,7 +7,7 @@ import { BehaviorSubject, Observable } from 'rxjs';
export class EcommerceProductsService implements Resolve<any> export class EcommerceProductsService implements Resolve<any>
{ {
products: any[]; products: any[];
onProductsChanged: BehaviorSubject<any> = new BehaviorSubject({}); onProductsChanged: BehaviorSubject<any>;
/** /**
* Constructor * Constructor
@ -18,6 +18,8 @@ export class EcommerceProductsService implements Resolve<any>
private _httpClient: HttpClient private _httpClient: HttpClient
) )
{ {
// Set the defaults
this.onProductsChanged = new BehaviorSubject({});
} }
/** /**

View File

@ -6,8 +6,8 @@ import { Observable, BehaviorSubject } from 'rxjs';
@Injectable() @Injectable()
export class FileManagerService implements Resolve<any> export class FileManagerService implements Resolve<any>
{ {
onFilesChanged: BehaviorSubject<any> = new BehaviorSubject({}); onFilesChanged: BehaviorSubject<any>;
onFileSelected: BehaviorSubject<any> = new BehaviorSubject({}); onFileSelected: BehaviorSubject<any>;
/** /**
* Constructor * Constructor
@ -18,11 +18,14 @@ export class FileManagerService implements Resolve<any>
private _httpClient: HttpClient private _httpClient: HttpClient
) )
{ {
// Set the defaults
this.onFilesChanged = new BehaviorSubject({});
this.onFileSelected = new BehaviorSubject({});
} }
/** /**
* Resolver * Resolver
* *
* @param {ActivatedRouteSnapshot} route * @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state * @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any} * @returns {Observable<any> | Promise<any> | any}

View File

@ -2,7 +2,7 @@
<mat-toolbar class="mat-accent m-0"> <mat-toolbar class="mat-accent m-0">
<mat-toolbar-row fxFlex fxLayout="row" fxLayoutAlign="space-between center"> <mat-toolbar-row fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<span class="title dialog-title">New Message</span> <span class="title dialog-title">New Message</span>
<button mat-icon-button (click)="dialogRef.close()" aria-label="Close dialog"> <button mat-icon-button (click)="_matDialogRef.close()" aria-label="Close dialog">
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
</mat-toolbar-row> </mat-toolbar-row>
@ -86,7 +86,7 @@
<div mat-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="space-between center"> <div mat-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="space-between center">
<div> <div>
<button mat-raised-button <button mat-raised-button
(click)="dialogRef.close(['send',composeForm])" (click)="_matDialogRef.close(['send',composeForm])"
class="save-button mat-accent" class="save-button mat-accent"
[disabled]="composeForm.invalid" [disabled]="composeForm.invalid"
aria-label="SAVE"> aria-label="SAVE">
@ -98,7 +98,7 @@
</button> </button>
</div> </div>
<button mat-icon-button (click)="dialogRef.close(['delete',composeForm])" aria-label="Delete" <button mat-icon-button (click)="_matDialogRef.close(['delete',composeForm])" aria-label="Delete"
matTooltip="Delete"> matTooltip="Delete">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</button> </button>

View File

@ -3,27 +3,44 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
@Component({ @Component({
selector : 'fuse-mail-compose', selector : 'mail-compose',
templateUrl : './compose.component.html', templateUrl : './compose.component.html',
styleUrls : ['./compose.component.scss'], styleUrls : ['./compose.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class FuseMailNgrxComposeDialogComponent export class MailNgrxComposeDialogComponent
{ {
composeForm: FormGroup; composeForm: FormGroup;
/**
* Constructor
*
* @param {MatDialogRef<MailNgrxComposeDialogComponent>} _matDialogRef
* @param _data
* @param {FormBuilder} _formBuilder
*/
constructor( constructor(
public dialogRef: MatDialogRef<FuseMailNgrxComposeDialogComponent>, private _matDialogRef: MatDialogRef<MailNgrxComposeDialogComponent>,
@Inject(MAT_DIALOG_DATA) private data: any, @Inject(MAT_DIALOG_DATA) private _data: any,
private formBuilder: FormBuilder private _formBuilder: FormBuilder
) )
{ {
// Set the defaults
this.composeForm = this.createComposeForm(); this.composeForm = this.createComposeForm();
} }
createComposeForm() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Create compose form
*
* @returns {FormGroup}
*/
createComposeForm(): FormGroup
{ {
return this.formBuilder.group({ return this._formBuilder.group({
from : { from : {
value : ['johndoe@creapond.com'], value : ['johndoe@creapond.com'],
disabled: [true] disabled: [true]

View File

@ -2,38 +2,62 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/c
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Mail } from '../mail.model'; import { Mail } from 'app/main/apps/mail-ngrx/mail.model';
import * as fromStore from '../store'; import * as fromStore from 'app/main/apps/mail-ngrx/store';
import { MailNgrxService } from '../mail.service'; import { MailNgrxService } from 'app/main/apps/mail-ngrx/mail.service';
@Component({ @Component({
selector : 'fuse-mail-details', selector : 'mail-details',
templateUrl : './mail-details.component.html', templateUrl : './mail-details.component.html',
styleUrls : ['./mail-details.component.scss'], styleUrls : ['./mail-details.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class FuseMailNgrxDetailsComponent implements OnChanges export class MailNgrxDetailsComponent implements OnChanges
{ {
labels$: Observable<any>; @Input('mail')
@Input('mail') mailInput: Mail; mailInput: Mail;
mail: Mail;
showDetails = false;
labels$: Observable<any>;
mail: Mail;
showDetails: boolean;
/**
* Constructor
*
* @param {MailNgrxService} _mailNgrxService
* @param {Store<MailAppState>} _store
*/
constructor( constructor(
private mailService: MailNgrxService, private _mailNgrxService: MailNgrxService,
private store: Store<fromStore.MailAppState> private _store: Store<fromStore.MailAppState>
) )
{ {
this.labels$ = this.store.select(fromStore.getLabelsArr); // Set the defaults
this.labels$ = this._store.select(fromStore.getLabelsArr);
this.showDetails = false;
} }
ngOnChanges() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*/
ngOnChanges(): void
{ {
this.updateModel(this.mailInput); this.updateModel(this.mailInput);
this.markAsRead(); this.markAsRead();
} }
markAsRead() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Mark as read
*/
markAsRead(): void
{ {
if ( this.mail && !this.mail.read ) if ( this.mail && !this.mail.read )
{ {
@ -42,28 +66,46 @@ export class FuseMailNgrxDetailsComponent implements OnChanges
} }
} }
toggleStar(event) /**
* Toggle star
*
* @param event
*/
toggleStar(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.mail.toggleStar(); this.mail.toggleStar();
this.updateMail(); this.updateMail();
} }
toggleImportant(event) /**
* Toggle important
*
* @param event
*/
toggleImportant(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.mail.toggleImportant(); this.mail.toggleImportant();
this.updateMail(); this.updateMail();
} }
updateModel(data) /**
* Update model
*
* @param data
*/
updateModel(data): void
{ {
this.mail = !data ? null : new Mail({...data}); this.mail = !data ? null : new Mail({...data});
} }
updateMail() /**
* Update the mail
*/
updateMail(): void
{ {
this.store.dispatch(new fromStore.UpdateMail(this.mail)); this._store.dispatch(new fromStore.UpdateMail(this.mail));
this.updateModel(this.mail); this.updateModel(this.mail);
} }
} }

View File

@ -7,12 +7,12 @@ import { Mail } from '../../mail.model';
import * as fromStore from '../../store'; import * as fromStore from '../../store';
@Component({ @Component({
selector : 'fuse-mail-list-item', selector : 'mail-list-item',
templateUrl : './mail-list-item.component.html', templateUrl : './mail-list-item.component.html',
styleUrls : ['./mail-list-item.component.scss'], styleUrls : ['./mail-list-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class FuseMailNgrxListItemComponent implements OnInit export class MailNgrxListItemComponent implements OnInit
{ {
@Input() mail: Mail; @Input() mail: Mail;
@HostBinding('class.selected') selected: boolean; @HostBinding('class.selected') selected: boolean;

View File

@ -3,7 +3,7 @@
</div> </div>
<div class="mail-list"> <div class="mail-list">
<fuse-mail-list-item matRipple *ngFor="let mail of mails" [mail]="mail" (click)="readMail(mail.id)" <mail-list-item matRipple *ngFor="let mail of mails" [mail]="mail" (click)="readMail(mail.id)"
[ngClass]="{'current-mail':mail?.id == currentMail?.id}"> [ngClass]="{'current-mail':mail?.id == currentMail?.id}">
</fuse-mail-list-item> </mail-list-item>
</div> </div>

View File

@ -1,49 +1,64 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Mail } from '../mail.model'; import { Mail } from 'app/main/apps/mail-ngrx/mail.model';
import { MailNgrxService } from '../mail.service'; import { MailNgrxService } from 'app/main/apps/mail-ngrx/mail.service';
@Component({ @Component({
selector : 'fuse-mail-list', selector : 'mail-list',
templateUrl : './mail-list.component.html', templateUrl : './mail-list.component.html',
styleUrls : ['./mail-list.component.scss'], styleUrls : ['./mail-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class FuseMailNgrxListComponent export class MailNgrxListComponent
{ {
@Input() mails: Mail[]; @Input()
@Input() currentMail: Mail[]; mails: Mail[];
@Input()
currentMail: Mail[];
/**
* Constructor
*
* @param {ActivatedRoute} _activatedRoute
* @param {MailNgrxService} _mailNgrxService
* @param {Router} _router
*/
constructor( constructor(
private route: ActivatedRoute, private _activatedRoute: ActivatedRoute,
private mailService: MailNgrxService, private _mailNgrxService: MailNgrxService,
private router: Router private _router: Router
) )
{ {
} }
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/** /**
* Read mail * Read mail
*
* @param mailId * @param mailId
*/ */
readMail(mailId) readMail(mailId): void
{ {
const labelHandle = this.route.snapshot.params.labelHandle, const labelHandle = this._activatedRoute.snapshot.params.labelHandle,
filterHandle = this.route.snapshot.params.filterHandle, filterHandle = this._activatedRoute.snapshot.params.filterHandle,
folderHandle = this.route.snapshot.params.folderHandle; folderHandle = this._activatedRoute.snapshot.params.folderHandle;
if ( labelHandle ) if ( labelHandle )
{ {
this.router.navigate(['apps/mail-ngrx/label/' + labelHandle + '/' + mailId]); this._router.navigate(['apps/mail-ngrx/label/' + labelHandle + '/' + mailId]);
} }
else if ( filterHandle ) else if ( filterHandle )
{ {
this.router.navigate(['apps/mail-ngrx/filter/' + filterHandle + '/' + mailId]); this._router.navigate(['apps/mail-ngrx/filter/' + filterHandle + '/' + mailId]);
} }
else else
{ {
this.router.navigate(['apps/mail-ngrx/' + folderHandle + '/' + mailId]); this._router.navigate(['apps/mail-ngrx/' + folderHandle + '/' + mailId]);
} }
} }
} }

View File

@ -9,7 +9,7 @@
<!-- SIDENAV --> <!-- SIDENAV -->
<mat-sidenav class="sidenav mat-sidenav-opened" position="start" mode="side" opened="true" <mat-sidenav class="sidenav mat-sidenav-opened" position="start" mode="side" opened="true"
fuseMatSidenavHelper="carded-left-sidenav" mat-is-locked-open="gt-md"> fuseMatSidenavHelper="carded-left-sidenav" mat-is-locked-open="gt-md">
<fuse-mail-main-sidenav></fuse-mail-main-sidenav> <mail-main-sidenav></mail-main-sidenav>
</mat-sidenav> </mat-sidenav>
<!-- / SIDENAV --> <!-- / SIDENAV -->
@ -88,7 +88,7 @@
</div> </div>
<div *ngIf="currentMail$ | async" fxHide.gt-xs> <div *ngIf="currentMail$ | async" fxHide.gt-xs>
<button mat-icon-button (click)="deSelectCurrentMail()"> <button mat-icon-button (click)="deselectCurrentMail()">
<mat-icon>arrow_back</mat-icon> <mat-icon>arrow_back</mat-icon>
</button> </button>
</div> </div>
@ -98,8 +98,8 @@
<!-- CONTENT --> <!-- CONTENT -->
<div class="content" fxLayout="row"> <div class="content" fxLayout="row">
<fuse-mail-list fusePerfectScrollbar fxFlex [mails]="mails$ | async" [currentMail]="currentMail$ | async"></fuse-mail-list> <mail-list fusePerfectScrollbar fxFlex [mails]="mails$ | async" [currentMail]="currentMail$ | async"></mail-list>
<fuse-mail-details [mail]="currentMail$ | async" fusePerfectScrollbar fxFlex></fuse-mail-details> <mail-details [mail]="currentMail$ | async" fusePerfectScrollbar fxFlex></mail-details>
</div> </div>
<!-- / CONTENT --> <!-- / CONTENT -->

View File

@ -40,16 +40,16 @@
@include media-breakpoint(xs) { @include media-breakpoint(xs) {
fuse-mail-list { mail-list {
border-right: none; border-right: none;
} }
fuse-mail-list, mail-list,
fuse-mail-details { mail-details {
flex: 1 0 100%; flex: 1 0 100%;
} }
fuse-mail-details { mail-details {
display: none !important; display: none !important;
} }
@ -65,11 +65,11 @@
.content { .content {
fuse-mail-list { mail-list {
display: none !important; display: none !important;
} }
fuse-mail-details { mail-details {
display: flex !important; display: flex !important;
} }
} }

View File

@ -10,16 +10,17 @@ import { FuseConfigService } from '@fuse/services/config.service';
import { Mail } from 'app/main/apps/mail-ngrx/mail.model'; import { Mail } from 'app/main/apps/mail-ngrx/mail.model';
import { MailNgrxService } from 'app/main/apps/mail-ngrx/mail.service'; import { MailNgrxService } from 'app/main/apps/mail-ngrx/mail.service';
import * as fromStore from 'app/main/apps/mail-ngrx/store'; import * as fromStore from 'app/main/apps/mail-ngrx/store';
import { locale as english } from 'app/main/apps/mail-ngrx/i18n/en'; import { locale as english } from 'app/main/apps/mail-ngrx/i18n/en';
import { locale as turkish } from 'app/main/apps/mail-ngrx/i18n/tr'; import { locale as turkish } from 'app/main/apps/mail-ngrx/i18n/tr';
@Component({ @Component({
selector : 'fuse-mail', selector : 'mail-ngrx',
templateUrl : './mail.component.html', templateUrl : './mail.component.html',
styleUrls : ['./mail.component.scss'], styleUrls : ['./mail.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class FuseMailNgrxComponent implements OnInit, OnDestroy export class MailNgrxComponent implements OnInit, OnDestroy
{ {
hasSelectedMails: boolean; hasSelectedMails: boolean;
isIndeterminate: boolean; isIndeterminate: boolean;
@ -33,31 +34,49 @@ export class FuseMailNgrxComponent implements OnInit, OnDestroy
mails: Mail[]; mails: Mail[];
selectedMailIds: string[]; selectedMailIds: string[];
/**
* Constructor
*
* @param {FuseConfigService} _fuseConfigService
* @param {MailNgrxService} _mailNgrxService
* @param {FuseTranslationLoaderService} _fuseTranslationLoaderService
* @param {Store<MailAppState>} _store
* @param {ChangeDetectorRef} _changeDetectorRef
*/
constructor( constructor(
private configService: FuseConfigService, private _fuseConfigService: FuseConfigService,
private mailService: MailNgrxService, private _mailNgrxService: MailNgrxService,
private translationLoader: FuseTranslationLoaderService, private _fuseTranslationLoaderService: FuseTranslationLoaderService,
private store: Store<fromStore.MailAppState>, private _store: Store<fromStore.MailAppState>,
private cd: ChangeDetectorRef private _changeDetectorRef: ChangeDetectorRef
) )
{ {
this.searchInput = new FormControl(''); // Configure the layout
this.translationLoader.loadTranslations(english, turkish); this._fuseConfigService.config = {
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 = [];
this.configService.config = {
routerAnimation: 'none' routerAnimation: 'none'
}; };
// Set the defaults
this.searchInput = new FormControl('');
this._fuseTranslationLoaderService.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() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.mails$.subscribe(mails => { this.mails$.subscribe(mails => {
this.mails = mails; this.mails = mails;
@ -79,16 +98,28 @@ export class FuseMailNgrxComponent implements OnInit, OnDestroy
debounceTime(300), debounceTime(300),
distinctUntilChanged() distinctUntilChanged()
).subscribe(searchText => { ).subscribe(searchText => {
this.store.dispatch(new fromStore.SetSearchText(searchText)); this._store.dispatch(new fromStore.SetSearchText(searchText));
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.cd.detach(); this._changeDetectorRef.detach();
} }
toggleSelectAll(ev) // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Toggle select all
*
* @param ev
*/
toggleSelectAll(ev): void
{ {
ev.preventDefault(); ev.preventDefault();
@ -102,41 +133,69 @@ export class FuseMailNgrxComponent implements OnInit, OnDestroy
} }
} }
selectAllMails() /**
* Select all mails
*/
selectAllMails(): void
{ {
this.store.dispatch(new fromStore.SelectAllMails()); this._store.dispatch(new fromStore.SelectAllMails());
} }
deselectAllMails() /**
* Deselect all mails
*/
deselectAllMails(): void
{ {
this.store.dispatch(new fromStore.DeselectAllMails()); this._store.dispatch(new fromStore.DeselectAllMails());
} }
selectMailsByParameter(parameter, value) /**
* Select mails by parameter
*
* @param parameter
* @param value
*/
selectMailsByParameter(parameter, value): void
{ {
this.store.dispatch(new fromStore.SelectMailsByParameter({ this._store.dispatch(new fromStore.SelectMailsByParameter({
parameter, parameter,
value value
})); }));
} }
toggleLabelOnSelectedMails(labelId) /**
* Toggle label on selected mails
*
* @param labelId
*/
toggleLabelOnSelectedMails(labelId): void
{ {
this.store.dispatch(new fromStore.AddLabelOnSelectedMails(labelId)); this._store.dispatch(new fromStore.AddLabelOnSelectedMails(labelId));
} }
setFolderOnSelectedMails(folderId) /**
* Set folder on selected mails
*
* @param folderId
*/
setFolderOnSelectedMails(folderId): void
{ {
this.store.dispatch(new fromStore.SetFolderOnSelectedMails(folderId)); this._store.dispatch(new fromStore.SetFolderOnSelectedMails(folderId));
} }
deSelectCurrentMail() /**
* Deselect current mail
*/
deselectCurrentMail(): void
{ {
this.store.dispatch(new fromStore.SetCurrentMail('')); this._store.dispatch(new fromStore.SetCurrentMail(''));
} }
refresh() /**
* Refresh
*/
refresh(): void
{ {
this.cd.markForCheck(); this._changeDetectorRef.markForCheck();
} }
} }

View File

@ -27,6 +27,11 @@ export class Mail
labels: string[]; labels: string[];
folder: string; folder: string;
/**
* Constructor
*
* @param mail
*/
constructor(mail) constructor(mail)
{ {
this.id = mail.id; this.id = mail.id;
@ -44,22 +49,34 @@ export class Mail
this.folder = mail.folder; this.folder = mail.folder;
} }
toggleStar() /**
* Toggle star
*/
toggleStar(): void
{ {
this.starred = !this.starred; this.starred = !this.starred;
} }
toggleImportant() /**
* Toggle important
*/
toggleImportant(): void
{ {
this.important = !this.important; this.important = !this.important;
} }
markRead() /**
* Mark as read
*/
markRead(): void
{ {
this.read = true; this.read = true;
} }
markUnRead() /**
* Mark as unread
*/
markUnread(): void
{ {
this.read = false; this.read = false;
} }

View File

@ -5,45 +5,45 @@ import { TranslateModule } from '@ngx-translate/core';
import { FuseSharedModule } from '@fuse/shared.module'; import { FuseSharedModule } from '@fuse/shared.module';
import { MailAppStoreModule } from 'app/main/apps/mail-ngrx/store/store.module';
import * as fromGuards from 'app/main/apps/mail-ngrx/store/guards/index'; import * as fromGuards from 'app/main/apps/mail-ngrx/store/guards/index';
import { FuseMailNgrxComponent } from 'app/main/apps/mail-ngrx/mail.component'; import { MailNgrxStoreModule } from 'app/main/apps/mail-ngrx/store/store.module';
import { FuseMailNgrxListComponent } from 'app/main/apps/mail-ngrx/mail-list/mail-list.component'; import { MailNgrxComponent } from 'app/main/apps/mail-ngrx/mail.component';
import { FuseMailNgrxListItemComponent } from 'app/main/apps/mail-ngrx/mail-list/mail-list-item/mail-list-item.component'; import { MailNgrxListComponent } from 'app/main/apps/mail-ngrx/mail-list/mail-list.component';
import { FuseMailNgrxDetailsComponent } from 'app/main/apps/mail-ngrx/mail-details/mail-details.component'; import { MailNgrxListItemComponent } from 'app/main/apps/mail-ngrx/mail-list/mail-list-item/mail-list-item.component';
import { FuseMailNgrxMainSidenavComponent } from 'app/main/apps/mail-ngrx/sidenavs/main/main-sidenav.component'; import { MailNgrxDetailsComponent } from 'app/main/apps/mail-ngrx/mail-details/mail-details.component';
import { FuseMailNgrxComposeDialogComponent } from 'app/main/apps/mail-ngrx/dialogs/compose/compose.component'; import { MailNgrxMainSidenavComponent } from 'app/main/apps/mail-ngrx/sidenavs/main/main-sidenav.component';
import { MailNgrxComposeDialogComponent } from 'app/main/apps/mail-ngrx/dialogs/compose/compose.component';
import { MailNgrxService } from 'app/main/apps/mail-ngrx/mail.service'; import { MailNgrxService } from 'app/main/apps/mail-ngrx/mail.service';
const routes: Routes = [ const routes: Routes = [
{ {
path : 'label/:labelHandle', path : 'label/:labelHandle',
component : FuseMailNgrxComponent, component : MailNgrxComponent,
canActivate: [fromGuards.ResolveGuard] canActivate: [fromGuards.ResolveGuard]
}, },
{ {
path : 'label/:labelHandle/:mailId', path : 'label/:labelHandle/:mailId',
component : FuseMailNgrxComponent, component : MailNgrxComponent,
canActivate: [fromGuards.ResolveGuard] canActivate: [fromGuards.ResolveGuard]
}, },
{ {
path : 'filter/:filterHandle', path : 'filter/:filterHandle',
component: FuseMailNgrxComponent, component : MailNgrxComponent,
canActivate: [fromGuards.ResolveGuard] canActivate: [fromGuards.ResolveGuard]
}, },
{ {
path : 'filter/:filterHandle/:mailId', path : 'filter/:filterHandle/:mailId',
component: FuseMailNgrxComponent, component : MailNgrxComponent,
canActivate: [fromGuards.ResolveGuard] canActivate: [fromGuards.ResolveGuard]
}, },
{ {
path : ':folderHandle', path : ':folderHandle',
component: FuseMailNgrxComponent, component : MailNgrxComponent,
canActivate: [fromGuards.ResolveGuard] canActivate: [fromGuards.ResolveGuard]
}, },
{ {
path : ':folderHandle/:mailId', path : ':folderHandle/:mailId',
component: FuseMailNgrxComponent, component : MailNgrxComponent,
canActivate: [fromGuards.ResolveGuard] canActivate: [fromGuards.ResolveGuard]
}, },
{ {
@ -54,12 +54,12 @@ const routes: Routes = [
@NgModule({ @NgModule({
declarations : [ declarations : [
FuseMailNgrxComponent, MailNgrxComponent,
FuseMailNgrxListComponent, MailNgrxListComponent,
FuseMailNgrxListItemComponent, MailNgrxListItemComponent,
FuseMailNgrxDetailsComponent, MailNgrxDetailsComponent,
FuseMailNgrxMainSidenavComponent, MailNgrxMainSidenavComponent,
FuseMailNgrxComposeDialogComponent MailNgrxComposeDialogComponent
], ],
imports : [ imports : [
RouterModule.forChild(routes), RouterModule.forChild(routes),
@ -80,13 +80,13 @@ const routes: Routes = [
FuseSharedModule, FuseSharedModule,
MailAppStoreModule MailNgrxStoreModule
], ],
providers : [ providers : [
MailNgrxService, MailNgrxService,
fromGuards.ResolveGuard fromGuards.ResolveGuard
], ],
entryComponents: [FuseMailNgrxComposeDialogComponent] entryComponents: [MailNgrxComposeDialogComponent]
}) })
export class FuseMailNgrxModule export class FuseMailNgrxModule
{ {

View File

@ -16,72 +16,108 @@ export class MailNgrxService
selectedMails: Mail[]; selectedMails: Mail[];
mails: Mail[]; mails: Mail[];
/**
* Constructor
*
* @param {HttpClient} _httpClient
* @param {Store<MailAppState>} _store
*/
constructor( constructor(
private http: HttpClient, private _httpClient: HttpClient,
private store: Store<MailAppState> private _store: Store<MailAppState>
) )
{ {
this.store.select(getFoldersArr).subscribe(folders => { this._store.select(getFoldersArr).subscribe(folders => {
this.foldersArr = folders; this.foldersArr = folders;
}); });
this.store.select(getFiltersArr).subscribe(filters => {
this._store.select(getFiltersArr).subscribe(filters => {
this.filtersArr = filters; this.filtersArr = filters;
}); });
this.store.select(getLabelsArr).subscribe(labels => {
this._store.select(getLabelsArr).subscribe(labels => {
this.labelsArr = labels; this.labelsArr = labels;
}); });
this.store.select(getMailsArr).subscribe(mails => {
this._store.select(getMailsArr).subscribe(mails => {
this.mails = mails; this.mails = mails;
}); });
this.selectedMails = []; this.selectedMails = [];
} }
/**
* Get all mails
*
* @returns {Observable<Mail[]>}
*/
getAllMails(): Observable<Mail[]> getAllMails(): Observable<Mail[]>
{ {
return this.http.get<Mail[]>('api/mail-mails'); return this._httpClient.get<Mail[]>('api/mail-mails');
} }
/**
* Get folders
*
* @returns {Observable<any>}
*/
getFolders(): Observable<any> getFolders(): Observable<any>
{ {
return this.http.get('api/mail-folders'); return this._httpClient.get('api/mail-folders');
} }
/**
* Get filters
*
* @returns {Observable<any>}
*/
getFilters(): Observable<any> getFilters(): Observable<any>
{ {
return this.http.get('api/mail-filters'); return this._httpClient.get('api/mail-filters');
} }
/**
* Get labels
*
* @returns {Observable<any>}
*/
getLabels(): Observable<any> getLabels(): Observable<any>
{ {
return this.http.get('api/mail-labels'); return this._httpClient.get('api/mail-labels');
} }
/**
* Get mails
*
* @param handle
* @returns {Observable<Mail[]>}
*/
getMails(handle): Observable<Mail[]> getMails(handle): Observable<Mail[]>
{ {
if ( handle.id === 'labelHandle' ) if ( handle.id === 'labelHandle' )
{ {
const labelId = this.labelsArr.find(label => label.handle === handle.value).id; const labelId = this.labelsArr.find(label => label.handle === handle.value).id;
return this.http.get<Mail[]>('api/mail-mails?labels=' + labelId); return this._httpClient.get<Mail[]>('api/mail-mails?labels=' + labelId);
} }
else if ( handle.id === 'filterHandle' ) else if ( handle.id === 'filterHandle' )
{ {
return this.http.get<Mail[]>('api/mail-mails?' + handle.value + '=true'); return this._httpClient.get<Mail[]>('api/mail-mails?' + handle.value + '=true');
} }
else // folderHandle else // folderHandle
{ {
const folderId = this.foldersArr.find(folder => folder.handle === handle.value).id; const folderId = this.foldersArr.find(folder => folder.handle === handle.value).id;
return this.http.get<any>('api/mail-mails?folder=' + folderId); return this._httpClient.get<any>('api/mail-mails?folder=' + folderId);
} }
} }
/** /**
* Update the mail * Update the mail
*
* @param mail * @param mail
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
updateMail(mail) updateMail(mail): any
{ {
return this.http.post('api/mail-mails/' + mail.id, {...mail}); return this._httpClient.post('api/mail-mails/' + mail.id, {...mail});
} }
} }

View File

@ -4,17 +4,17 @@ import { FormGroup } from '@angular/forms';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { MailNgrxService } from '../../mail.service'; import { MailNgrxService } from 'app/main/apps/mail-ngrx/mail.service';
import * as fromStore from './../../store'; import * as fromStore from 'app/main/apps/mail-ngrx/store';
import { FuseMailNgrxComposeDialogComponent } from '../../dialogs/compose/compose.component'; import { MailNgrxComposeDialogComponent } from 'app/main/apps/mail-ngrx/dialogs/compose/compose.component';
@Component({ @Component({
selector : 'fuse-mail-main-sidenav', selector : 'mail-main-sidenav',
templateUrl : './main-sidenav.component.html', templateUrl : './main-sidenav.component.html',
styleUrls : ['./main-sidenav.component.scss'], styleUrls : ['./main-sidenav.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class FuseMailNgrxMainSidenavComponent export class MailNgrxMainSidenavComponent
{ {
labels: any[]; labels: any[];
accounts: object; accounts: object;
@ -25,30 +25,43 @@ export class FuseMailNgrxMainSidenavComponent
filters$: Observable<any>; filters$: Observable<any>;
labels$: Observable<any>; labels$: Observable<any>;
/**
* Constructor
*
* @param {MailNgrxService} _mailNgrxService
* @param {MatDialog} _matDialog
* @param {Store<MailAppState>} _store
*/
constructor( constructor(
private mailService: MailNgrxService, private _mailNgrxService: MailNgrxService,
public dialog: MatDialog, private _matDialog: MatDialog,
private store: Store<fromStore.MailAppState> private _store: Store<fromStore.MailAppState>
) )
{ {
// Data // Set the defaults
this.accounts = { this.accounts = {
'creapond' : 'johndoe@creapond.com', 'creapond' : 'johndoe@creapond.com',
'withinpixels': 'johndoe@withinpixels.com' 'withinpixels': 'johndoe@withinpixels.com'
}; };
this.selectedAccount = 'creapond'; this.selectedAccount = 'creapond';
this.folders$ = this._store.select(fromStore.getFoldersArr);
this.folders$ = this.store.select(fromStore.getFoldersArr); this.filters$ = this._store.select(fromStore.getFiltersArr);
this.filters$ = this.store.select(fromStore.getFiltersArr); this.labels$ = this._store.select(fromStore.getLabelsArr);
this.labels$ = this.store.select(fromStore.getLabelsArr);
} }
composeDialog() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Compose dialog
*/
composeDialog(): void
{ {
this.dialogRef = this.dialog.open(FuseMailNgrxComposeDialogComponent, { this.dialogRef = this._matDialog.open(MailNgrxComposeDialogComponent, {
panelClass: 'mail-compose-dialog' panelClass: 'mail-compose-dialog'
}); });
this.dialogRef.afterClosed() this.dialogRef.afterClosed()
.subscribe(response => { .subscribe(response => {
if ( !response ) if ( !response )

View File

@ -16,11 +16,16 @@ export class ResolveGuard implements CanActivate
{ {
routerState: any; routerState: any;
/**
* Constructor
*
* @param {Store<MailAppState>} _store
*/
constructor( constructor(
private store: Store<MailAppState> private _store: Store<MailAppState>
) )
{ {
this.store.select(getRouterState).subscribe(routerState => { this._store.select(getRouterState).subscribe(routerState => {
if ( routerState ) if ( routerState )
{ {
this.routerState = routerState.state; this.routerState = routerState.state;
@ -28,6 +33,13 @@ export class ResolveGuard implements CanActivate
}); });
} }
/**
* Can activate
*
* @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state
* @returns {Observable<boolean>}
*/
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>
{ {
return this.checkStore().pipe( return this.checkStore().pipe(
@ -36,32 +48,42 @@ export class ResolveGuard implements CanActivate
); );
} }
/**
* Check store
*
* @returns {Observable<any>}
*/
checkStore(): Observable<any> checkStore(): Observable<any>
{ {
return forkJoin( return forkJoin(
this.getFolders(), this.getFolders(),
this.getFilters(), this.getFilters(),
this.getLabels() this.getLabels()
) )
.pipe( .pipe(
filter(([foldersLoaded, filtersLoaded, labelsLoaded]) => filtersLoaded && foldersLoaded && labelsLoaded), filter(([foldersLoaded, filtersLoaded, labelsLoaded]) => !!(foldersLoaded && filtersLoaded && labelsLoaded)),
take(1), take(1),
switchMap(() => switchMap(() =>
this.getMails() this.getMails()
), ),
take(1), take(1),
map(() => this.store.dispatch(new fromStore.SetCurrentMail(this.routerState.params.mailId))) map(() => this._store.dispatch(new fromStore.SetCurrentMail(this.routerState.params.mailId)))
); );
} }
getFolders() /**
* Get folders
*
* @returns {Observable<any>}
*/
getFolders(): any
{ {
return this.store.select(getFoldersLoaded) return this._store.select(getFoldersLoaded)
.pipe( .pipe(
tap(loaded => { tap(loaded => {
if ( !loaded ) if ( !loaded )
{ {
this.store.dispatch(new fromStore.GetFolders([])); this._store.dispatch(new fromStore.GetFolders([]));
} }
}), }),
filter(loaded => loaded), filter(loaded => loaded),
@ -71,16 +93,17 @@ export class ResolveGuard implements CanActivate
/** /**
* Get Filters * Get Filters
*
* @returns {Observable<any>} * @returns {Observable<any>}
*/ */
getFilters() getFilters(): any
{ {
return this.store.select(getFiltersLoaded) return this._store.select(getFiltersLoaded)
.pipe( .pipe(
tap(loaded => { tap(loaded => {
if ( !loaded ) if ( !loaded )
{ {
this.store.dispatch(new fromStore.GetFilters([])); this._store.dispatch(new fromStore.GetFilters([]));
} }
}), }),
filter(loaded => loaded), filter(loaded => loaded),
@ -92,14 +115,14 @@ export class ResolveGuard implements CanActivate
* Get Labels * Get Labels
* @returns {Observable<any>} * @returns {Observable<any>}
*/ */
getLabels() getLabels(): any
{ {
return this.store.select(getLabelsLoaded) return this._store.select(getLabelsLoaded)
.pipe( .pipe(
tap(loaded => { tap(loaded => {
if ( !loaded ) if ( !loaded )
{ {
this.store.dispatch(new fromStore.GetLabels([])); this._store.dispatch(new fromStore.GetLabels([]));
} }
}), }),
filter(loaded => loaded), filter(loaded => loaded),
@ -109,19 +132,20 @@ export class ResolveGuard implements CanActivate
/** /**
* Get Mails * Get Mails
*
* @returns {Observable<any>} * @returns {Observable<any>}
*/ */
getMails() getMails(): any
{ {
return this.store.select(getMailsLoaded) return this._store.select(getMailsLoaded)
.pipe( .pipe(
tap((loaded: any) => { tap((loaded: any) => {
if ( !this.routerState.params[loaded.id] || this.routerState.params[loaded.id] !== loaded.value ) if ( !this.routerState.params[loaded.id] || this.routerState.params[loaded.id] !== loaded.value )
{ {
this.store.dispatch(new fromStore.GetMails()); this._store.dispatch(new fromStore.GetMails());
this.store.dispatch(new fromStore.SetSearchText('')); this._store.dispatch(new fromStore.SetSearchText(''));
this.store.dispatch(new fromStore.DeselectAllMails()); this._store.dispatch(new fromStore.DeselectAllMails());
} }
}), }),
filter((loaded: any) => { filter((loaded: any) => {

View File

@ -2,7 +2,7 @@ import * as FiltersActions from 'app/main/apps/mail-ngrx/store/actions/filters.a
export interface FiltersState export interface FiltersState
{ {
entities: { [id: number]: any }; entities?: { [id: number]: any };
loading: boolean; loading: boolean;
loaded: boolean; loaded: boolean;
} }

View File

@ -2,7 +2,7 @@ import * as FoldersActions from 'app/main/apps/mail-ngrx/store/actions/folders.a
export interface FoldersState export interface FoldersState
{ {
entities: { [id: number]: any }; entities?: { [id: number]: any };
loading: boolean; loading: boolean;
loaded: boolean; loaded: boolean;
} }

View File

@ -2,7 +2,7 @@ import * as LabelsActions from 'app/main/apps/mail-ngrx/store/actions/labels.act
export interface LabelsState export interface LabelsState
{ {
entities: { [id: number]: any }; entities?: { [id: number]: any };
loading: boolean; loading: boolean;
loaded: boolean; loaded: boolean;
} }

View File

@ -3,7 +3,7 @@ import { Mail } from 'app/main/apps/mail-ngrx/mail.model';
export interface MailsState export interface MailsState
{ {
entities: { [id: number]: Mail }; entities?: { [id: number]: Mail };
currentMail: any; currentMail: any;
selectedMailIds: string[]; selectedMailIds: string[];
searchText: string; searchText: string;

View File

@ -12,6 +12,6 @@ import { effects } from 'app/main/apps/mail-ngrx/store/effects';
], ],
providers: [] providers: []
}) })
export class MailAppStoreModule export class MailNgrxStoreModule
{ {
} }

View File

@ -2,7 +2,7 @@
<mat-toolbar class="mat-accent m-0"> <mat-toolbar class="mat-accent m-0">
<mat-toolbar-row fxFlex fxLayout="row" fxLayoutAlign="space-between center"> <mat-toolbar-row fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<span class="title dialog-title">New Message</span> <span class="title dialog-title">New Message</span>
<button mat-icon-button (click)="dialogRef.close()" aria-label="Close dialog"> <button mat-icon-button (click)="_matDialogRef.close()" aria-label="Close dialog">
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
</mat-toolbar-row> </mat-toolbar-row>
@ -91,7 +91,7 @@
<div mat-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="space-between center"> <div mat-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="space-between center">
<div> <div>
<button mat-raised-button <button mat-raised-button
(click)="dialogRef.close(['send',composeForm])" (click)="_matDialogRef.close(['send',composeForm])"
class="save-button mat-accent" class="save-button mat-accent"
[disabled]="composeForm.invalid" [disabled]="composeForm.invalid"
aria-label="SAVE"> aria-label="SAVE">
@ -103,7 +103,7 @@
</button> </button>
</div> </div>
<button mat-icon-button (click)="dialogRef.close(['delete',composeForm])" <button mat-icon-button (click)="_matDialogRef.close(['delete',composeForm])"
aria-label="Delete" aria-label="Delete"
matTooltip="Delete"> matTooltip="Delete">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>

View File

@ -3,25 +3,42 @@ import { FormControl, FormGroup } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
@Component({ @Component({
selector : 'fuse-mail-compose', selector : 'mail-compose',
templateUrl : './compose.component.html', templateUrl : './compose.component.html',
styleUrls : ['./compose.component.scss'], styleUrls : ['./compose.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class FuseMailComposeDialogComponent export class MailComposeDialogComponent
{ {
showExtraToFields = false; showExtraToFields: boolean;
composeForm: FormGroup; composeForm: FormGroup;
/**
* Constructor
*
* @param {MatDialogRef<MailComposeDialogComponent>} _matDialogRef
* @param _data
*/
constructor( constructor(
public dialogRef: MatDialogRef<FuseMailComposeDialogComponent>, private _matDialogRef: MatDialogRef<MailComposeDialogComponent>,
@Inject(MAT_DIALOG_DATA) private data: any @Inject(MAT_DIALOG_DATA) private _data: any
) )
{ {
// Set the defaults
this.composeForm = this.createComposeForm(); this.composeForm = this.createComposeForm();
this.showExtraToFields = false;
} }
createComposeForm() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Create compose form
*
* @returns {FormGroup}
*/
createComposeForm(): FormGroup
{ {
return new FormGroup({ return new FormGroup({
from : new FormControl({ from : new FormControl({
@ -36,7 +53,10 @@ export class FuseMailComposeDialogComponent
}); });
} }
toggleExtraToFields() /**
* Toggle extra to fields
*/
toggleExtraToFields(): void
{ {
this.showExtraToFields = !this.showExtraToFields; this.showExtraToFields = !this.showExtraToFields;
} }

View File

@ -1,70 +1,106 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { Mail } from '../mail.model'; import { Mail } from 'app/main/apps/mail/mail.model';
import { MailService } from '../mail.service'; import { MailService } from 'app/main/apps/mail/mail.service';
@Component({ @Component({
selector : 'fuse-mail-details', selector : 'mail-details',
templateUrl: './mail-details.component.html', templateUrl: './mail-details.component.html',
styleUrls : ['./mail-details.component.scss'], styleUrls : ['./mail-details.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseMailDetailsComponent implements OnInit, OnDestroy export class MailDetailsComponent implements OnInit, OnDestroy
{ {
mail: Mail; mail: Mail;
labels: any[]; labels: any[];
showDetails = false; showDetails: boolean;
onCurrentMailChanged: Subscription; // Private
onLabelsChanged: Subscription; private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {MailService} _mailService
*/
constructor( constructor(
private mailService: MailService private _mailService: MailService
) )
{ {
// Set the defaults
this.showDetails = false;
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
// Subscribe to update the current mail // Subscribe to update the current mail
this.onCurrentMailChanged = this._mailService.onCurrentMailChanged
this.mailService.onCurrentMailChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(currentMail => { .subscribe(currentMail => {
this.mail = currentMail; this.mail = currentMail;
}); });
// Subscribe to update on label change // Subscribe to update on label change
this.onLabelsChanged = this._mailService.onLabelsChanged
this.mailService.onLabelsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(labels => { .subscribe(labels => {
this.labels = labels; this.labels = labels;
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onCurrentMailChanged.unsubscribe(); // Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
toggleStar(event) // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Toggle star
*
* @param event
*/
toggleStar(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.mail.toggleStar(); this.mail.toggleStar();
this.mailService.updateMail(this.mail); this._mailService.updateMail(this.mail);
} }
toggleImportant(event) /**
* Toggle important
*
* @param event
*/
toggleImportant(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.mail.toggleImportant(); this.mail.toggleImportant();
this.mailService.updateMail(this.mail); this._mailService.updateMail(this.mail);
} }
} }

View File

@ -1,94 +1,125 @@
import { Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Mail } from '../../mail.model'; import { Mail } from 'app/main/apps/mail/mail.model';
import { MailService } from '../../mail.service'; import { MailService } from 'app/main/apps/mail/mail.service';
@Component({ @Component({
selector : 'fuse-mail-list-item', selector : 'mail-list-item',
templateUrl: './mail-list-item.component.html', templateUrl: './mail-list-item.component.html',
styleUrls : ['./mail-list-item.component.scss'] styleUrls : ['./mail-list-item.component.scss']
}) })
export class FuseMailListItemComponent implements OnInit, OnDestroy export class MailListItemComponent implements OnInit, OnDestroy
{ {
@Input() mail: Mail; @Input() mail: Mail;
labels: any[]; labels: any[];
@HostBinding('class.selected') selected: boolean;
onSelectedMailsChanged: Subscription; @HostBinding('class.selected')
onLabelsChanged: Subscription; selected: boolean;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {MailService} _mailService
*/
constructor( constructor(
private mailService: MailService private _mailService: MailService
) )
{ {
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
// Set the initial values // Set the initial values
this.mail = new Mail(this.mail); this.mail = new Mail(this.mail);
// Subscribe to update on selected mail change // Subscribe to update on selected mail change
this.onSelectedMailsChanged = this._mailService.onSelectedMailsChanged
this.mailService.onSelectedMailsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(selectedMails => { .subscribe(selectedMails => {
this.selected = false; this.selected = false;
if ( selectedMails.length > 0 ) if ( selectedMails.length > 0 )
{
for ( const mail of selectedMails )
{ {
for ( const mail of selectedMails ) if ( mail.id === this.mail.id )
{ {
if ( mail.id === this.mail.id ) this.selected = true;
{ break;
this.selected = true;
break;
}
} }
} }
}); }
});
// Subscribe to update on label change // Subscribe to update on label change
this.onLabelsChanged = this._mailService.onLabelsChanged
this.mailService.onLabelsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(labels => { .subscribe(labels => {
this.labels = labels; this.labels = labels;
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onSelectedMailsChanged.unsubscribe(); // Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
onSelectedChange() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* On selected change
*/
onSelectedChange(): void
{ {
this.mailService.toggleSelectedMail(this.mail.id); this._mailService.toggleSelectedMail(this.mail.id);
} }
/** /**
* Toggle star * Toggle star
*
* @param event * @param event
*/ */
toggleStar(event) toggleStar(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.mail.toggleStar(); this.mail.toggleStar();
this.mailService.updateMail(this.mail); this._mailService.updateMail(this.mail);
} }
/** /**
* Toggle Important * Toggle Important
*
* @param event * @param event
*/ */
toggleImportant(event) toggleImportant(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.mail.toggleImportant(); this.mail.toggleImportant();
this.mailService.updateMail(this.mail); this._mailService.updateMail(this.mail);
} }
} }

View File

@ -3,8 +3,8 @@
</div> </div>
<div class="mail-list" *fuseIfOnDom [@animateStagger]="{value:'50'}"> <div class="mail-list" *fuseIfOnDom [@animateStagger]="{value:'50'}">
<fuse-mail-list-item matRipple *ngFor="let mail of mails" [mail]="mail" (click)="readMail(mail.id)" <mail-list-item matRipple *ngFor="let mail of mails" [mail]="mail" (click)="readMail(mail.id)"
[ngClass]="{'current-mail':mail?.id == currentMail?.id}" [ngClass]="{'current-mail':mail?.id == currentMail?.id}"
[@animate]="{value:'*',params:{y:'100%'}}"> [@animate]="{value:'*',params:{y:'100%'}}">
</fuse-mail-list-item> </mail-list-item>
</div> </div>

View File

@ -1,109 +1,134 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { Mail } from '../mail.model'; import { Mail } from 'app/main/apps/mail/mail.model';
import { MailService } from '../mail.service'; import { MailService } from 'app/main/apps/mail/mail.service';
@Component({ @Component({
selector : 'fuse-mail-list', selector : 'mail-list',
templateUrl: './mail-list.component.html', templateUrl: './mail-list.component.html',
styleUrls : ['./mail-list.component.scss'], styleUrls : ['./mail-list.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseMailListComponent implements OnInit, OnDestroy export class MailListComponent implements OnInit, OnDestroy
{ {
mails: Mail[]; mails: Mail[];
currentMail: Mail; currentMail: Mail;
onMailsChanged: Subscription; // Private
onCurrentMailChanged: Subscription; private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {ActivatedRoute} _activatedRoute
* @param {MailService} _mailService
* @param {Location} _location
*/
constructor( constructor(
private route: ActivatedRoute, private _activatedRoute: ActivatedRoute,
private mailService: MailService, private _mailService: MailService,
private location: Location private _location: Location
) )
{ {
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
// Subscribe to update mails on changes // Subscribe to update mails on changes
this.onMailsChanged = this._mailService.onMailsChanged
this.mailService.onMailsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(mails => { .subscribe(mails => {
this.mails = mails; this.mails = mails;
}); });
// Subscribe to update current mail on changes // Subscribe to update current mail on changes
this.onCurrentMailChanged = this._mailService.onCurrentMailChanged
this.mailService.onCurrentMailChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(currentMail => { .subscribe(currentMail => {
if ( !currentMail ) if ( !currentMail )
{
// Set the current mail id to null to deselect the current mail
this.currentMail = null;
// Handle the location changes
const labelHandle = this._activatedRoute.snapshot.params.labelHandle,
filterHandle = this._activatedRoute.snapshot.params.filterHandle,
folderHandle = this._activatedRoute.snapshot.params.folderHandle;
if ( labelHandle )
{ {
// Set the current mail id to null to deselect the current mail this._location.go('apps/mail/label/' + labelHandle);
this.currentMail = null; }
else if ( filterHandle )
// Handle the location changes {
const labelHandle = this.route.snapshot.params.labelHandle, this._location.go('apps/mail/filter/' + filterHandle);
filterHandle = this.route.snapshot.params.filterHandle,
folderHandle = this.route.snapshot.params.folderHandle;
if ( labelHandle )
{
this.location.go('apps/mail/label/' + labelHandle);
}
else if ( filterHandle )
{
this.location.go('apps/mail/filter/' + filterHandle);
}
else
{
this.location.go('apps/mail/' + folderHandle);
}
} }
else else
{ {
this.currentMail = currentMail; this._location.go('apps/mail/' + folderHandle);
} }
}); }
} else
{
ngOnDestroy() this.currentMail = currentMail;
{ }
this.onMailsChanged.unsubscribe(); });
this.onCurrentMailChanged.unsubscribe();
} }
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/** /**
* Read mail * Read mail
*
* @param mailId * @param mailId
*/ */
readMail(mailId) readMail(mailId): void
{ {
const labelHandle = this.route.snapshot.params.labelHandle, const labelHandle = this._activatedRoute.snapshot.params.labelHandle,
filterHandle = this.route.snapshot.params.filterHandle, filterHandle = this._activatedRoute.snapshot.params.filterHandle,
folderHandle = this.route.snapshot.params.folderHandle; folderHandle = this._activatedRoute.snapshot.params.folderHandle;
if ( labelHandle ) if ( labelHandle )
{ {
this.location.go('apps/mail/label/' + labelHandle + '/' + mailId); this._location.go('apps/mail/label/' + labelHandle + '/' + mailId);
} }
else if ( filterHandle ) else if ( filterHandle )
{ {
this.location.go('apps/mail/filter/' + filterHandle + '/' + mailId); this._location.go('apps/mail/filter/' + filterHandle + '/' + mailId);
} }
else else
{ {
this.location.go('apps/mail/' + folderHandle + '/' + mailId); this._location.go('apps/mail/' + folderHandle + '/' + mailId);
} }
// Set current mail // Set current mail
this.mailService.setCurrentMail(mailId); this._mailService.setCurrentMail(mailId);
} }
} }

View File

@ -9,7 +9,7 @@
<!-- SIDENAV --> <!-- SIDENAV -->
<mat-sidenav class="sidenav" position="start" mode="side" opened="true" <mat-sidenav class="sidenav" position="start" mode="side" opened="true"
fuseMatSidenavHelper="carded-left-sidenav" mat-is-locked-open="gt-md"> fuseMatSidenavHelper="carded-left-sidenav" mat-is-locked-open="gt-md">
<fuse-mail-main-sidenav></fuse-mail-main-sidenav> <mail-main-sidenav></mail-main-sidenav>
</mat-sidenav> </mat-sidenav>
<!-- / SIDENAV --> <!-- / SIDENAV -->
@ -86,7 +86,7 @@
</div> </div>
<div *ngIf="currentMail" fxHide.gt-xs> <div *ngIf="currentMail" fxHide.gt-xs>
<button mat-icon-button (click)="deSelectCurrentMail()"> <button mat-icon-button (click)="deselectCurrentMail()">
<mat-icon>arrow_back</mat-icon> <mat-icon>arrow_back</mat-icon>
</button> </button>
</div> </div>
@ -96,8 +96,8 @@
<!-- CONTENT --> <!-- CONTENT -->
<div class="content" fxLayout="row"> <div class="content" fxLayout="row">
<fuse-mail-list fusePerfectScrollbar fxFlex></fuse-mail-list> <mail-list fusePerfectScrollbar fxFlex></mail-list>
<fuse-mail-details fusePerfectScrollbar fxFlex></fuse-mail-details> <mail-details fusePerfectScrollbar fxFlex></mail-details>
</div> </div>
<!-- / CONTENT --> <!-- / CONTENT -->

View File

@ -40,16 +40,16 @@
@include media-breakpoint(xs) { @include media-breakpoint(xs) {
fuse-mail-list { mail-list {
border-right: none; border-right: none;
} }
fuse-mail-list, mail-list,
fuse-mail-details { mail-details {
flex: 1 0 100%; flex: 1 0 100%;
} }
fuse-mail-details { mail-details {
display: none !important; display: none !important;
} }
@ -65,11 +65,11 @@
.content { .content {
fuse-mail-list { mail-list {
display: none !important; display: none !important;
} }
fuse-mail-details { mail-details {
display: flex !important; display: flex !important;
} }
} }

View File

@ -1,22 +1,23 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { Subject } from 'rxjs';
import { Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { FuseTranslationLoaderService } from '@fuse/services/translation-loader.service'; import { FuseTranslationLoaderService } from '@fuse/services/translation-loader.service';
import { Mail } from './mail.model'; import { Mail } from 'app/main/apps/mail/mail.model';
import { MailService } from './mail.service'; import { MailService } from 'app/main/apps/mail/mail.service';
import { locale as english } from './i18n/en';
import { locale as turkish } from './i18n/tr'; import { locale as english } from 'app/main/apps/mail//i18n/en';
import { locale as turkish } from 'app/main/apps/mail//i18n/tr';
import { FuseConfigService } from '@fuse/services/config.service';
@Component({ @Component({
selector : 'fuse-mail', selector : 'mail',
templateUrl: './mail.component.html', templateUrl: './mail.component.html',
styleUrls : ['./mail.component.scss'] styleUrls : ['./mail.component.scss']
}) })
export class FuseMailComponent implements OnInit, OnDestroy export class MailComponent implements OnInit, OnDestroy
{ {
hasSelectedMails: boolean; hasSelectedMails: boolean;
isIndeterminate: boolean; isIndeterminate: boolean;
@ -26,110 +27,162 @@ export class FuseMailComponent implements OnInit, OnDestroy
searchInput: FormControl; searchInput: FormControl;
currentMail: Mail; currentMail: Mail;
onSelectedMailsChanged: Subscription; // Private
onFoldersChanged: Subscription; private _unsubscribeAll: Subject<any>;
onFiltersChanged: Subscription;
onLabelsChanged: Subscription;
onCurrentMailChanged: Subscription;
/**
* Constructor
*
* @param {MailService} _mailService
* @param {FuseConfigService} _fuseConfigService
* @param {FuseTranslationLoaderService} _fuseTranslationLoaderService
*/
constructor( constructor(
private mailService: MailService, private _mailService: MailService,
private fuseTranslationLoader: FuseTranslationLoaderService private _fuseConfigService: FuseConfigService,
private _fuseTranslationLoaderService: FuseTranslationLoaderService
) )
{ {
// Configure the layout
this._fuseConfigService.config = {
routerAnimation: 'none'
};
// Load the translations
this._fuseTranslationLoaderService.loadTranslations(english, turkish);
// Set the defaults
this.searchInput = new FormControl(''); this.searchInput = new FormControl('');
this.fuseTranslationLoader.loadTranslations(english, turkish);
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onSelectedMailsChanged = this._mailService.onSelectedMailsChanged
this.mailService.onSelectedMailsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(selectedMails => { .subscribe(selectedMails => {
setTimeout(() => {
this.hasSelectedMails = selectedMails.length > 0;
this.isIndeterminate = (selectedMails.length !== this._mailService.mails.length && selectedMails.length > 0);
}, 0);
});
setTimeout(() => { this._mailService.onFoldersChanged
this.hasSelectedMails = selectedMails.length > 0; .pipe(takeUntil(this._unsubscribeAll))
this.isIndeterminate = (selectedMails.length !== this.mailService.mails.length && selectedMails.length > 0); .subscribe(folders => {
}, 0); this.folders = this._mailService.folders;
}); });
this.onFoldersChanged = this._mailService.onFiltersChanged
this.mailService.onFoldersChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(folders => { .subscribe(folders => {
this.folders = this.mailService.folders; this.filters = this._mailService.filters;
}); });
this.onFiltersChanged = this._mailService.onLabelsChanged
this.mailService.onFiltersChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(folders => { .subscribe(labels => {
this.filters = this.mailService.filters; this.labels = this._mailService.labels;
}); });
this.onLabelsChanged = this._mailService.onCurrentMailChanged
this.mailService.onLabelsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(labels => { .subscribe(currentMail => {
this.labels = this.mailService.labels; if ( !currentMail )
}); {
this.currentMail = null;
this.onCurrentMailChanged = }
this.mailService.onCurrentMailChanged else
.subscribe(currentMail => { {
if ( !currentMail ) this.currentMail = currentMail;
{ }
this.currentMail = null; });
}
else
{
this.currentMail = currentMail;
}
});
this.searchInput.valueChanges.pipe( this.searchInput.valueChanges.pipe(
takeUntil(this._unsubscribeAll),
debounceTime(300), debounceTime(300),
distinctUntilChanged() distinctUntilChanged()
) )
.subscribe(searchText => { .subscribe(searchText => {
this.mailService.onSearchTextChanged.next(searchText); this._mailService.onSearchTextChanged.next(searchText);
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onSelectedMailsChanged.unsubscribe(); // Unsubscribe from all subscriptions
this.onFoldersChanged.unsubscribe(); this._unsubscribeAll.next();
this.onFiltersChanged.unsubscribe(); this._unsubscribeAll.complete();
this.onLabelsChanged.unsubscribe();
this.onCurrentMailChanged.unsubscribe();
} }
toggleSelectAll() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Toggle select all
*/
toggleSelectAll(): void
{ {
this.mailService.toggleSelectAll(); this._mailService.toggleSelectAll();
} }
selectMails(filterParameter?, filterValue?) /**
* Select mails
*
* @param filterParameter
* @param filterValue
*/
selectMails(filterParameter?, filterValue?): void
{ {
this.mailService.selectMails(filterParameter, filterValue); this._mailService.selectMails(filterParameter, filterValue);
} }
deselectMails() /**
* Deselect mails
*/
deselectMails(): void
{ {
this.mailService.deselectMails(); this._mailService.deselectMails();
} }
deSelectCurrentMail() /**
* Deselect current mail
*/
deselectCurrentMail(): void
{ {
this.mailService.onCurrentMailChanged.next(null); this._mailService.onCurrentMailChanged.next(null);
} }
toggleLabelOnSelectedMails(labelId) /**
* Toggle label on selected mails
*
* @param labelId
*/
toggleLabelOnSelectedMails(labelId): void
{ {
this.mailService.toggleLabelOnSelectedMails(labelId); this._mailService.toggleLabelOnSelectedMails(labelId);
} }
setFolderOnSelectedMails(folderId) /**
* Set folder on selected mails
*
* @param folderId
*/
setFolderOnSelectedMails(folderId): void
{ {
this.mailService.setFolderOnSelectedMails(folderId); this._mailService.setFolderOnSelectedMails(folderId);
} }
} }

View File

@ -27,6 +27,11 @@ export class Mail
labels: string[]; labels: string[];
folder: string; folder: string;
/**
* Constructor
*
* @param mail
*/
constructor(mail) constructor(mail)
{ {
this.id = mail.id; this.id = mail.id;

View File

@ -1,59 +1,57 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { MatButtonModule, MatCheckboxModule, MatDialogModule, MatFormFieldModule, MatIconModule, MatInputModule, MatMenuModule, MatRippleModule, MatSelectModule, MatSidenavModule, MatToolbarModule } from '@angular/material'; import { MatButtonModule, MatCheckboxModule, MatDialogModule, MatFormFieldModule, MatIconModule, MatInputModule, MatMenuModule, MatRippleModule, MatSelectModule, MatSidenavModule, MatToolbarModule } from '@angular/material';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { FuseSharedModule } from '@fuse/shared.module'; import { FuseSharedModule } from '@fuse/shared.module';
import { MailService } from './mail.service'; import { MailService } from 'app/main/apps/mail/mail.service';
import { FuseMailComponent } from './mail.component'; import { MailComponent } from 'app/main/apps/mail/mail.component';
import { FuseMailMainSidenavComponent } from './sidenavs/main/main-sidenav.component'; import { MailListComponent } from 'app/main/apps/mail/mail-list/mail-list.component';
import { FuseMailListItemComponent } from './mail-list/mail-list-item/mail-list-item.component'; import { MailListItemComponent } from 'app/main/apps/mail/mail-list/mail-list-item/mail-list-item.component';
import { FuseMailListComponent } from './mail-list/mail-list.component'; import { MailDetailsComponent } from 'app/main/apps/mail/mail-details/mail-details.component';
import { FuseMailDetailsComponent } from './mail-details/mail-details.component'; import { MailMainSidenavComponent } from 'app/main/apps/mail/sidenavs/main/main-sidenav.component';
import { FuseMailComposeDialogComponent } from './dialogs/compose/compose.component'; import { MailComposeDialogComponent } from 'app/main/apps/mail/dialogs/compose/compose.component';
const routes: Routes = [ const routes: Routes = [
{ {
path : 'label/:labelHandle', path : 'label/:labelHandle',
component: FuseMailComponent, component: MailComponent,
resolve : { resolve : {
mail: MailService mail: MailService
} }
}, },
{ {
path : 'label/:labelHandle/:mailId', path : 'label/:labelHandle/:mailId',
component: FuseMailComponent, component: MailComponent,
resolve : { resolve : {
mail: MailService mail: MailService
} }
}, },
{ {
path : 'filter/:filterHandle', path : 'filter/:filterHandle',
component: FuseMailComponent, component: MailComponent,
resolve : { resolve : {
mail: MailService mail: MailService
} }
}, },
{ {
path : 'filter/:filterHandle/:mailId', path : 'filter/:filterHandle/:mailId',
component: FuseMailComponent, component: MailComponent,
resolve : { resolve : {
mail: MailService mail: MailService
} }
}, },
{ {
path : ':folderHandle', path : ':folderHandle',
component: FuseMailComponent, component: MailComponent,
resolve : { resolve : {
mail: MailService mail: MailService
} }
}, },
{ {
path : ':folderHandle/:mailId', path : ':folderHandle/:mailId',
component: FuseMailComponent, component: MailComponent,
resolve : { resolve : {
mail: MailService mail: MailService
} }
@ -66,12 +64,12 @@ const routes: Routes = [
@NgModule({ @NgModule({
declarations : [ declarations : [
FuseMailComponent, MailComponent,
FuseMailListComponent, MailListComponent,
FuseMailListItemComponent, MailListItemComponent,
FuseMailDetailsComponent, MailDetailsComponent,
FuseMailMainSidenavComponent, MailMainSidenavComponent,
FuseMailComposeDialogComponent MailComposeDialogComponent
], ],
imports : [ imports : [
RouterModule.forChild(routes), RouterModule.forChild(routes),
@ -95,8 +93,10 @@ const routes: Routes = [
providers : [ providers : [
MailService MailService
], ],
entryComponents: [FuseMailComposeDialogComponent] entryComponents: [
MailComposeDialogComponent
]
}) })
export class FuseMailModule export class MailModule
{ {
} }

View File

@ -5,7 +5,7 @@ import { BehaviorSubject, Observable } from 'rxjs';
import { FuseUtils } from '@fuse/utils'; import { FuseUtils } from '@fuse/utils';
import { Mail } from './mail.model'; import { Mail } from 'app/main/apps/mail/mail.model';
@Injectable() @Injectable()
export class MailService implements Resolve<any> export class MailService implements Resolve<any>
@ -20,22 +20,37 @@ export class MailService implements Resolve<any>
labels: any[]; labels: any[];
routeParams: any; routeParams: any;
onMailsChanged: BehaviorSubject<any> = new BehaviorSubject([]); onMailsChanged: BehaviorSubject<any>;
onSelectedMailsChanged: BehaviorSubject<any> = new BehaviorSubject([]); onSelectedMailsChanged: BehaviorSubject<any>;
onCurrentMailChanged: BehaviorSubject<any> = new BehaviorSubject([]); onCurrentMailChanged: BehaviorSubject<any>;
onFoldersChanged: BehaviorSubject<any>;
onFiltersChanged: BehaviorSubject<any>;
onLabelsChanged: BehaviorSubject<any>;
onSearchTextChanged: BehaviorSubject<any>;
onFoldersChanged: BehaviorSubject<any> = new BehaviorSubject([]); /**
onFiltersChanged: BehaviorSubject<any> = new BehaviorSubject([]); * Constructor
onLabelsChanged: BehaviorSubject<any> = new BehaviorSubject([]); *
onSearchTextChanged: BehaviorSubject<any> = new BehaviorSubject(''); * @param {HttpClient} _httpClient
*/
constructor(private http: HttpClient) constructor(
private _httpClient: HttpClient
)
{ {
// Set the defaults
this.selectedMails = []; this.selectedMails = [];
this.onMailsChanged = new BehaviorSubject([]);
this.onSelectedMailsChanged = new BehaviorSubject([]);
this.onCurrentMailChanged = new BehaviorSubject([]);
this.onFoldersChanged = new BehaviorSubject([]);
this.onFiltersChanged = new BehaviorSubject([]);
this.onLabelsChanged = new BehaviorSubject([]);
this.onSearchTextChanged = new BehaviorSubject('');
} }
/** /**
* Resolve * Resolver
*
* @param {ActivatedRouteSnapshot} route * @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state * @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any} * @returns {Observable<any> | Promise<any> | any}
@ -83,12 +98,13 @@ export class MailService implements Resolve<any>
/** /**
* Get all folders * Get all folders
*
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
getFolders(): Promise<any> getFolders(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/mail-folders') this._httpClient.get('api/mail-folders')
.subscribe((response: any) => { .subscribe((response: any) => {
this.folders = response; this.folders = response;
this.onFoldersChanged.next(this.folders); this.onFoldersChanged.next(this.folders);
@ -99,12 +115,13 @@ export class MailService implements Resolve<any>
/** /**
* Get all filters * Get all filters
*
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
getFilters(): Promise<any> getFilters(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/mail-filters') this._httpClient.get('api/mail-filters')
.subscribe((response: any) => { .subscribe((response: any) => {
this.filters = response; this.filters = response;
this.onFiltersChanged.next(this.filters); this.onFiltersChanged.next(this.filters);
@ -115,12 +132,13 @@ export class MailService implements Resolve<any>
/** /**
* Get all labels * Get all labels
*
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
getLabels(): Promise<any> getLabels(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/mail-labels') this._httpClient.get('api/mail-labels')
.subscribe((response: any) => { .subscribe((response: any) => {
this.labels = response; this.labels = response;
this.onLabelsChanged.next(this.labels); this.onLabelsChanged.next(this.labels);
@ -131,6 +149,7 @@ export class MailService implements Resolve<any>
/** /**
* Get all mails * Get all mails
*
* @returns {Promise<Mail[]>} * @returns {Promise<Mail[]>}
*/ */
getMails(): Promise<Mail[]> getMails(): Promise<Mail[]>
@ -150,6 +169,7 @@ export class MailService implements Resolve<any>
/** /**
* Get mails by folder * Get mails by folder
*
* @param handle * @param handle
* @returns {Promise<Mail[]>} * @returns {Promise<Mail[]>}
*/ */
@ -157,12 +177,12 @@ export class MailService implements Resolve<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/mail-folders?handle=' + handle) this._httpClient.get('api/mail-folders?handle=' + handle)
.subscribe((folders: any) => { .subscribe((folders: any) => {
const folderId = folders[0].id; const folderId = folders[0].id;
this.http.get('api/mail-mails?folder=' + folderId) this._httpClient.get('api/mail-mails?folder=' + folderId)
.subscribe((mails: any) => { .subscribe((mails: any) => {
this.mails = mails.map(mail => { this.mails = mails.map(mail => {
@ -182,6 +202,7 @@ export class MailService implements Resolve<any>
/** /**
* Get mails by filter * Get mails by filter
*
* @param handle * @param handle
* @returns {Promise<Mail[]>} * @returns {Promise<Mail[]>}
*/ */
@ -189,7 +210,7 @@ export class MailService implements Resolve<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/mail-mails?' + handle + '=true') this._httpClient.get('api/mail-mails?' + handle + '=true')
.subscribe((mails: any) => { .subscribe((mails: any) => {
this.mails = mails.map(mail => { this.mails = mails.map(mail => {
@ -208,18 +229,19 @@ export class MailService implements Resolve<any>
/** /**
* Get mails by label * Get mails by label
*
* @param handle * @param handle
* @returns {Promise<Mail[]>} * @returns {Promise<Mail[]>}
*/ */
getMailsByLabel(handle): Promise<Mail[]> getMailsByLabel(handle): Promise<Mail[]>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/mail-labels?handle=' + handle) this._httpClient.get('api/mail-labels?handle=' + handle)
.subscribe((labels: any) => { .subscribe((labels: any) => {
const labelId = labels[0].id; const labelId = labels[0].id;
this.http.get('api/mail-mails?labels=' + labelId) this._httpClient.get('api/mail-mails?labels=' + labelId)
.subscribe((mails: any) => { .subscribe((mails: any) => {
this.mails = mails.map(mail => { this.mails = mails.map(mail => {
@ -239,9 +261,10 @@ export class MailService implements Resolve<any>
/** /**
* Toggle selected mail by id * Toggle selected mail by id
*
* @param id * @param id
*/ */
toggleSelectedMail(id) toggleSelectedMail(id): void
{ {
// First, check if we already have that mail as selected... // First, check if we already have that mail as selected...
if ( this.selectedMails.length > 0 ) if ( this.selectedMails.length > 0 )
@ -281,7 +304,7 @@ export class MailService implements Resolve<any>
/** /**
* Toggle select all * Toggle select all
*/ */
toggleSelectAll() toggleSelectAll(): void
{ {
if ( this.selectedMails.length > 0 ) if ( this.selectedMails.length > 0 )
{ {
@ -294,7 +317,13 @@ export class MailService implements Resolve<any>
} }
selectMails(filterParameter?, filterValue?) /**
* Select mails
*
* @param filterParameter
* @param filterValue
*/
selectMails(filterParameter?, filterValue?): void
{ {
this.selectedMails = []; this.selectedMails = [];
@ -316,7 +345,10 @@ export class MailService implements Resolve<any>
this.onSelectedMailsChanged.next(this.selectedMails); this.onSelectedMailsChanged.next(this.selectedMails);
} }
deselectMails() /**
* Deselect mails
*/
deselectMails(): void
{ {
this.selectedMails = []; this.selectedMails = [];
@ -326,9 +358,10 @@ export class MailService implements Resolve<any>
/** /**
* Set current mail by id * Set current mail by id
*
* @param id * @param id
*/ */
setCurrentMail(id) setCurrentMail(id): void
{ {
this.currentMail = this.mails.find(mail => { this.currentMail = this.mails.find(mail => {
return mail.id === id; return mail.id === id;
@ -339,9 +372,10 @@ export class MailService implements Resolve<any>
/** /**
* Toggle label on selected mails * Toggle label on selected mails
*
* @param labelId * @param labelId
*/ */
toggleLabelOnSelectedMails(labelId) toggleLabelOnSelectedMails(labelId): void
{ {
this.selectedMails.map(mail => { this.selectedMails.map(mail => {
@ -362,9 +396,10 @@ export class MailService implements Resolve<any>
/** /**
* Set folder on selected mails * Set folder on selected mails
*
* @param folderId * @param folderId
*/ */
setFolderOnSelectedMails(folderId) setFolderOnSelectedMails(folderId): void
{ {
this.selectedMails.map(mail => { this.selectedMails.map(mail => {
mail.folder = folderId; mail.folder = folderId;
@ -377,14 +412,15 @@ export class MailService implements Resolve<any>
/** /**
* Update the mail * Update the mail
*
* @param mail * @param mail
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
updateMail(mail) updateMail(mail): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.post('api/mail-mails/' + mail.id, {...mail}) this._httpClient.post('api/mail-mails/' + mail.id, {...mail})
.subscribe(response => { .subscribe(response => {
this.getMails().then(mails => { this.getMails().then(mails => {
@ -400,5 +436,4 @@ export class MailService implements Resolve<any>
}); });
}); });
} }
} }

View File

@ -1,20 +1,21 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material'; import { MatDialog } from '@angular/material';
import { Subscription } from 'rxjs';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { MailService } from '../../mail.service'; import { MailService } from 'app/main/apps/mail/mail.service';
import { FuseMailComposeDialogComponent } from '../../dialogs/compose/compose.component'; import { MailComposeDialogComponent } from 'app/main/apps/mail/dialogs/compose/compose.component';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector : 'fuse-mail-main-sidenav', selector : 'mail-main-sidenav',
templateUrl: './main-sidenav.component.html', templateUrl: './main-sidenav.component.html',
styleUrls : ['./main-sidenav.component.scss'], styleUrls : ['./main-sidenav.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseMailMainSidenavComponent implements OnInit, OnDestroy export class MailMainSidenavComponent implements OnInit, OnDestroy
{ {
folders: any[]; folders: any[];
filters: any[]; filters: any[];
@ -23,55 +24,79 @@ export class FuseMailMainSidenavComponent implements OnInit, OnDestroy
selectedAccount: string; selectedAccount: string;
dialogRef: any; dialogRef: any;
onFoldersChanged: Subscription; // Private
onFiltersChanged: Subscription; private _unsubscribeAll: Subject<any>;
onLabelsChanged: Subscription;
/**
* Constructor
*
* @param {MailService} _mailService
* @param {MatDialog} _matDialog
*/
constructor( constructor(
private mailService: MailService, private _mailService: MailService,
public dialog: MatDialog public _matDialog: MatDialog
) )
{ {
// Data // Set the defaults
this.accounts = { this.accounts = {
'creapond' : 'johndoe@creapond.com', 'creapond' : 'johndoe@creapond.com',
'withinpixels': 'johndoe@withinpixels.com' 'withinpixels': 'johndoe@withinpixels.com'
}; };
this.selectedAccount = 'creapond'; this.selectedAccount = 'creapond';
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onFoldersChanged = this._mailService.onFoldersChanged
this.mailService.onFoldersChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(folders => { .subscribe(folders => {
this.folders = folders; this.folders = folders;
}); });
this.onFiltersChanged = this._mailService.onFiltersChanged
this.mailService.onFiltersChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(filters => { .subscribe(filters => {
this.filters = filters; this.filters = filters;
}); });
this.onLabelsChanged = this._mailService.onLabelsChanged
this.mailService.onLabelsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(labels => { .subscribe(labels => {
this.labels = labels; this.labels = labels;
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onFoldersChanged.unsubscribe(); // Unsubscribe from all subscriptions
this.onFiltersChanged.unsubscribe(); this._unsubscribeAll.next();
this.onLabelsChanged.unsubscribe(); this._unsubscribeAll.complete();
} }
composeDialog() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Compose dialog
*/
composeDialog(): void
{ {
this.dialogRef = this.dialog.open(FuseMailComposeDialogComponent, { this.dialogRef = this._matDialog.open(MailComposeDialogComponent, {
panelClass: 'mail-compose-dialog' panelClass: 'mail-compose-dialog'
}); });
this.dialogRef.afterClosed() this.dialogRef.afterClosed()

View File

@ -67,6 +67,11 @@ export class Board
color: string color: string
}[]; }[];
/**
* Constructor
*
* @param board
*/
constructor(board) constructor(board)
{ {
this.name = board.name || 'Untitled Board'; this.name = board.name || 'Untitled Board';

View File

@ -2,49 +2,77 @@ import { Component, EventEmitter, Output, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
@Component({ @Component({
selector : 'fuse-scrumboard-board-add-list', selector : 'scrumboard-board-add-list',
templateUrl: './add-list.component.html', templateUrl: './add-list.component.html',
styleUrls : ['./add-list.component.scss'] styleUrls : ['./add-list.component.scss']
}) })
export class FuseScrumboardBoardAddListComponent export class ScrumboardBoardAddListComponent
{ {
formActive = false; formActive: boolean;
form: FormGroup; form: FormGroup;
@Output() onlistAdd = new EventEmitter();
@ViewChild('nameInput') nameInputField;
@Output()
onListAdd: EventEmitter<any>;
@ViewChild('nameInput')
nameInputField;
/**
* Constructor
*
* @param {FormBuilder} _formBuilder
*/
constructor( constructor(
private formBuilder: FormBuilder private _formBuilder: FormBuilder
) )
{ {
// Set the defaults
this.formActive = false;
this.onListAdd = new EventEmitter();
} }
openForm() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Open form
*/
openForm(): void
{ {
this.form = this.formBuilder.group({ this.form = this._formBuilder.group({
name: [''] name: ['']
}); });
this.formActive = true; this.formActive = true;
this.focusNameField(); this.focusNameField();
} }
closeForm() /**
* Close form
*/
closeForm(): void
{ {
this.formActive = false; this.formActive = false;
} }
focusNameField() /**
* Focus to the name field
*/
focusNameField(): void
{ {
setTimeout(() => { setTimeout(() => {
this.nameInputField.nativeElement.focus(); this.nameInputField.nativeElement.focus();
}); });
} }
onFormSubmit() /**
* On form submit
*/
onFormSubmit(): void
{ {
if ( this.form.valid ) if ( this.form.valid )
{ {
this.onlistAdd.next(this.form.getRawValue().name); this.onListAdd.next(this.form.getRawValue().name);
this.formActive = false; this.formActive = false;
} }
} }

View File

@ -26,10 +26,10 @@
fxFlex="1 0 100%" fxFlex.gt-xs="1 0 auto" fxFlex="1 0 100%" fxFlex.gt-xs="1 0 auto"
fxFlexOrder="1" fxFlexOrder.gt-xs="2"> fxFlexOrder="1" fxFlexOrder.gt-xs="2">
<mat-icon *ngIf="board.settings.subscribed" class="board-subscribe s-16">remove_red_eye</mat-icon> <mat-icon *ngIf="board.settings.subscribed" class="board-subscribe s-16">remove_red_eye</mat-icon>
<fuse-scrumboard-edit-board-name <scrumboard-edit-board-name
[board]="board" [board]="board"
(onNameChanged)="onBoardNameChanged($event)"> (onNameChanged)="onBoardNameChanged($event)">
</fuse-scrumboard-edit-board-name> </scrumboard-edit-board-name>
</div> </div>
<!-- / BOARD NAME --> <!-- / BOARD NAME -->
@ -58,21 +58,21 @@
*fuseIfOnDom [@animateStagger]="{value:'50'}"> *fuseIfOnDom [@animateStagger]="{value:'50'}">
<!-- LIST --> <!-- LIST -->
<fuse-scrumboard-board-list <scrumboard-board-list
class="scrumboard-board-list list-wrapper ngx-dnd-item" class="scrumboard-board-list list-wrapper ngx-dnd-item"
ngxDraggable ngxDraggable
*ngFor="let list of board.lists" *ngFor="let list of board.lists"
[model]="list" [model]="list"
[list]="list" [list]="list"
[@animate]="{value:'*',params:{duration:'350ms',x:'100%'}}"> [@animate]="{value:'*',params:{duration:'350ms',x:'100%'}}">
</fuse-scrumboard-board-list> </scrumboard-board-list>
<!-- / LIST --> <!-- / LIST -->
<!-- NEW LIST BUTTON--> <!-- NEW LIST BUTTON-->
<fuse-scrumboard-board-add-list class="new-list-wrapper" (onlistAdd)="onListAdd($event)" <scrumboard-board-add-list class="new-list-wrapper" (onListAdd)="onListAdd($event)"
ngxDraggable [moves]="false" ngxDraggable [moves]="false"
[@animate]="{value:'*',params:{duration:'350ms',x:'100%'}}"> [@animate]="{value:'*',params:{duration:'350ms',x:'100%'}}">
</fuse-scrumboard-board-add-list> </scrumboard-board-add-list>
<!-- / NEW LIST BUTTON--> <!-- / NEW LIST BUTTON-->
</div> </div>
@ -82,7 +82,7 @@
</div> </div>
<mat-sidenav #settingsSidenav position="end"> <mat-sidenav #settingsSidenav position="end">
<fuse-scrumboard-board-settings></fuse-scrumboard-board-settings> <scrumboard-board-settings></scrumboard-board-settings>
</mat-sidenav> </mat-sidenav>
</mat-sidenav-container> </mat-sidenav-container>

View File

@ -1,65 +1,100 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs';
import { Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { List } from '../list.model'; import { ScrumboardService } from 'app/main/apps/scrumboard/scrumboard.service';
import { ScrumboardService } from '../scrumboard.service'; import { List } from 'app/main/apps/scrumboard/list.model';
@Component({ @Component({
selector : 'fuse-scrumboard-board', selector : 'scrumboard-board',
templateUrl: './board.component.html', templateUrl: './board.component.html',
styleUrls : ['./board.component.scss'], styleUrls : ['./board.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseScrumboardBoardComponent implements OnInit, OnDestroy export class ScrumboardBoardComponent implements OnInit, OnDestroy
{ {
board: any; board: any;
onBoardChanged: Subscription;
// Private
private _unsubscribeAll: Subject<any>;
constructor( constructor(
private route: ActivatedRoute, private _activatedRoute: ActivatedRoute,
private location: Location, private _location: Location,
private scrumboardService: ScrumboardService private _scrumboardService: ScrumboardService
) )
{ {
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onBoardChanged = this._scrumboardService.onBoardChanged
this.scrumboardService.onBoardChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(board => { .subscribe(board => {
this.board = board; this.board = board;
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onBoardChanged.unsubscribe(); // Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
onListAdd(newListName) // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* On list add
*
* @param newListName
*/
onListAdd(newListName): void
{ {
if ( newListName === '' ) if ( newListName === '' )
{ {
return; return;
} }
this.scrumboardService.addList(new List({name: newListName})); this._scrumboardService.addList(new List({name: newListName}));
} }
onBoardNameChanged(newName) /**
* On board name changed
*
* @param newName
*/
onBoardNameChanged(newName): void
{ {
this.scrumboardService.updateBoard(); this._scrumboardService.updateBoard();
this.location.go('/apps/scrumboard/boards/' + this.board.id + '/' + this.board.uri); this._location.go('/apps/scrumboard/boards/' + this.board.id + '/' + this.board.uri);
} }
onDrop(ev) /**
* On drop
*
* @param ev
*/
onDrop(ev): void
{ {
this.scrumboardService.updateBoard(); this._scrumboardService.updateBoard();
} }
} }

View File

@ -35,8 +35,8 @@
<mat-menu #labelsMenu="matMenu" [overlapTrigger]="false" class="scrumboard-labels-menu"> <mat-menu #labelsMenu="matMenu" [overlapTrigger]="false" class="scrumboard-labels-menu">
<fuse-scrumboard-label-selector [card]="card" <scrumboard-label-selector [card]="card"
(onCardLabelsChange)="updateCard()"></fuse-scrumboard-label-selector> (onCardLabelsChange)="updateCard()"></scrumboard-label-selector>
</mat-menu> </mat-menu>
</div> </div>
@ -130,7 +130,7 @@
</div> </div>
<!-- CLOSE DIALOG BUTTON --> <!-- CLOSE DIALOG BUTTON -->
<button mat-icon-button (click)="dialogRef.close()" aria-label="Close Dialog"> <button mat-icon-button (click)="_matDialogRef.close()" aria-label="Close Dialog">
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
<!-- / CLOSE DIALOG BUTTON --> <!-- / CLOSE DIALOG BUTTON -->

View File

@ -1,74 +1,108 @@
import { Component, Inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { Component, Inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { NgForm } from '@angular/forms/src/forms'; import { NgForm } from '@angular/forms/src/forms';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef, MatMenuTrigger } from '@angular/material'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef, MatMenuTrigger } from '@angular/material';
import { Subject } from 'rxjs';
import { Subscription } from 'rxjs';
import { FuseConfirmDialogComponent } from '@fuse/components/confirm-dialog/confirm-dialog.component'; import { FuseConfirmDialogComponent } from '@fuse/components/confirm-dialog/confirm-dialog.component';
import { FuseUtils } from '@fuse/utils'; import { FuseUtils } from '@fuse/utils';
import { ScrumboardService } from '../../../scrumboard.service'; import { ScrumboardService } from 'app/main/apps/scrumboard/scrumboard.service';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector : 'fuse-scrumboard-board-card-dialog', selector : 'scrumboard-board-card-dialog',
templateUrl : './card.component.html', templateUrl : './card.component.html',
styleUrls : ['./card.component.scss'], styleUrls : ['./card.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy export class ScrumboardCardDialogComponent implements OnInit, OnDestroy
{ {
card: any; card: any;
board: any; board: any;
list: any; list: any;
onBoardChanged: Subscription;
toggleInArray = FuseUtils.toggleInArray; toggleInArray = FuseUtils.toggleInArray;
@ViewChild('checklistMenuTrigger') checklistMenu: MatMenuTrigger;
@ViewChild('newCheckListTitleField') newCheckListTitleField;
confirmDialogRef: MatDialogRef<FuseConfirmDialogComponent>; confirmDialogRef: MatDialogRef<FuseConfirmDialogComponent>;
@ViewChild('checklistMenuTrigger')
checklistMenu: MatMenuTrigger;
@ViewChild('newCheckListTitleField')
newCheckListTitleField;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {MatDialogRef<ScrumboardCardDialogComponent>} _matDialogRef
* @param _data
* @param {MatDialog} _matDialog
* @param {ScrumboardService} _scrumboardService
*/
constructor( constructor(
public dialogRef: MatDialogRef<FuseScrumboardCardDialogComponent>, private _matDialogRef: MatDialogRef<ScrumboardCardDialogComponent>,
@Inject(MAT_DIALOG_DATA) private data: any, @Inject(MAT_DIALOG_DATA) private _data: any,
public dialog: MatDialog, private _matDialog: MatDialog,
private scrumboardService: ScrumboardService private _scrumboardService: ScrumboardService
) )
{ {
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onBoardChanged = this._scrumboardService.onBoardChanged
this.scrumboardService.onBoardChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(board => { .subscribe(board => {
this.board = board; this.board = board;
this.card = this.board.cards.find((_card) => { this.card = this.board.cards.find((_card) => {
return this.data.cardId === _card.id; return this._data.cardId === _card.id;
});
this.list = this.board.lists.find((_list) => {
return this.data.listId === _list.id;
});
}); });
this.list = this.board.lists.find((_list) => {
return this._data.listId === _list.id;
});
});
} }
/** /**
* Remove Due date * On destroy
*/ */
removeDueDate() ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Remove due date
*/
removeDueDate(): void
{ {
this.card.due = ''; this.card.due = '';
this.updateCard(); this.updateCard();
} }
/** /**
* Toggle Subscribe * Toggle subscribe
*/ */
toggleSubscribe() toggleSubscribe(): void
{ {
this.card.subscribed = !this.card.subscribed; this.card.subscribed = !this.card.subscribed;
@ -76,10 +110,11 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
} }
/** /**
* Toggle Cover Image * Toggle cover image
*
* @param attachmentId * @param attachmentId
*/ */
toggleCoverImage(attachmentId) toggleCoverImage(attachmentId): void
{ {
if ( this.card.idAttachmentCover === attachmentId ) if ( this.card.idAttachmentCover === attachmentId )
{ {
@ -94,10 +129,11 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
} }
/** /**
* Remove Attachment * Remove attachment
*
* @param attachment * @param attachment
*/ */
removeAttachment(attachment) removeAttachment(attachment): void
{ {
if ( attachment.id === this.card.idAttachmentCover ) if ( attachment.id === this.card.idAttachmentCover )
{ {
@ -110,10 +146,11 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
} }
/** /**
* Remove Checklist * Remove checklist
*
* @param checklist * @param checklist
*/ */
removeChecklist(checklist) removeChecklist(checklist): void
{ {
this.card.checklists.splice(this.card.checklists.indexOf(checklist), 1); this.card.checklists.splice(this.card.checklists.indexOf(checklist), 1);
@ -121,10 +158,11 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
} }
/** /**
* Update Checked Count * Update checked count
*
* @param list * @param list
*/ */
updateCheckedCount(list) updateCheckedCount(list): void
{ {
const checkItems = list.checkItems; const checkItems = list.checkItems;
let checkedItems = 0; let checkedItems = 0;
@ -154,11 +192,12 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
} }
/** /**
* Remove Checklist Item * Remove checklist item
*
* @param checkItem * @param checkItem
* @param checklist * @param checklist
*/ */
removeChecklistItem(checkItem, checklist) removeChecklistItem(checkItem, checklist): void
{ {
checklist.checkItems.splice(checklist.checkItems.indexOf(checkItem), 1); checklist.checkItems.splice(checklist.checkItems.indexOf(checkItem), 1);
@ -168,11 +207,12 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
} }
/** /**
* Add Check Item * Add check item
*
* @param {NgForm} form * @param {NgForm} form
* @param checkList * @param checkList
*/ */
addCheckItem(form: NgForm, checkList) addCheckItem(form: NgForm, checkList): void
{ {
const checkItemVal = form.value.checkItem; const checkItemVal = form.value.checkItem;
@ -196,10 +236,11 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
} }
/** /**
* Add Checklist * Add checklist
*
* @param {NgForm} form * @param {NgForm} form
*/ */
addChecklist(form: NgForm) addChecklist(form: NgForm): void
{ {
this.card.checklists.push({ this.card.checklists.push({
id : FuseUtils.generateGUID(), id : FuseUtils.generateGUID(),
@ -215,9 +256,9 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
} }
/** /**
* On Checklist Menu Open * On checklist menu open
*/ */
onChecklistMenuOpen() onChecklistMenuOpen(): void
{ {
setTimeout(() => { setTimeout(() => {
this.newCheckListTitleField.nativeElement.focus(); this.newCheckListTitleField.nativeElement.focus();
@ -225,10 +266,11 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
} }
/** /**
* Add New Comment * Add new comment
*
* @param {NgForm} form * @param {NgForm} form
*/ */
addNewComment(form: NgForm) addNewComment(form: NgForm): void
{ {
const newCommentText = form.value.newComment; const newCommentText = form.value.newComment;
@ -246,11 +288,11 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
} }
/** /**
* Remove Card * Remove card
*/ */
removeCard() removeCard(): void
{ {
this.confirmDialogRef = this.dialog.open(FuseConfirmDialogComponent, { this.confirmDialogRef = this._matDialog.open(FuseConfirmDialogComponent, {
disableClose: false disableClose: false
}); });
@ -259,22 +301,17 @@ export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
this.confirmDialogRef.afterClosed().subscribe(result => { this.confirmDialogRef.afterClosed().subscribe(result => {
if ( result ) if ( result )
{ {
this.dialogRef.close(); this._matDialogRef.close();
this.scrumboardService.removeCard(this.card.id, this.list.id); this._scrumboardService.removeCard(this.card.id, this.list.id);
} }
}); });
} }
/** /**
* Update Card * Update card
*/ */
updateCard() updateCard(): void
{ {
this.scrumboardService.updateCard(this.card); this._scrumboardService.updateCard(this.card);
}
ngOnDestroy()
{
this.onBoardChanged.unsubscribe();
} }
} }

View File

@ -1,68 +1,110 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { Subject } from 'rxjs';
import { Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { FuseUtils } from '@fuse/utils'; import { FuseUtils } from '@fuse/utils';
import { ScrumboardService } from '../../../../scrumboard.service'; import { ScrumboardService } from 'app/main/apps/scrumboard/scrumboard.service';
@Component({ @Component({
selector : 'fuse-scrumboard-label-selector', selector : 'scrumboard-label-selector',
templateUrl : './label-selector.component.html', templateUrl : './label-selector.component.html',
styleUrls : ['./label-selector.component.scss'], styleUrls : ['./label-selector.component.scss'],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseScrumboardLabelSelectorComponent implements OnInit, OnDestroy export class ScrumboardLabelSelectorComponent implements OnInit, OnDestroy
{ {
@Input('card')
card: any;
@Output()
onCardLabelsChange: EventEmitter<any>;
board: any; board: any;
@Input('card') card: any; labelsMenuView: string;
@Output() onCardLabelsChange = new EventEmitter();
labelsMenuView = 'labels';
selectedLabel: any; selectedLabel: any;
newLabel = { newLabel: any;
'id' : '', toggleInArray: any;
'name' : '',
'color': 'mat-blue-400-bg'
};
toggleInArray = FuseUtils.toggleInArray;
onBoardChanged: Subscription; // Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {ScrumboardService} _scrumboardService
*/
constructor( constructor(
private scrumboardService: ScrumboardService private _scrumboardService: ScrumboardService
) )
{ {
// Set the defaults
this.onCardLabelsChange = new EventEmitter();
this.labelsMenuView = 'labels';
this.newLabel = {
'id' : '',
'name' : '',
'color': 'mat-blue-400-bg'
};
this.toggleInArray = FuseUtils.toggleInArray;
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onBoardChanged = this._scrumboardService.onBoardChanged
this.scrumboardService.onBoardChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(board => { .subscribe(board => {
this.board = board; this.board = board;
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onBoardChanged.unsubscribe(); // Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
cardLabelsChanged() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Card labels changed
*/
cardLabelsChanged(): void
{ {
this.onCardLabelsChange.next(); this.onCardLabelsChange.next();
} }
onLabelChange() /**
* On label change
*/
onLabelChange(): void
{ {
this.scrumboardService.updateBoard(); this._scrumboardService.updateBoard();
} }
addNewLabel() /**
* Add new label
*/
addNewLabel(): void
{ {
this.newLabel.id = FuseUtils.generateGUID(); this.newLabel.id = FuseUtils.generateGUID();
this.board.labels.push(Object.assign({}, this.newLabel)); this.board.labels.push(Object.assign({}, this.newLabel));

View File

@ -2,25 +2,41 @@ import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
@Component({ @Component({
selector : 'fuse-scrumboard-edit-board-name', selector : 'scrumboard-edit-board-name',
templateUrl: './edit-board-name.component.html', templateUrl: './edit-board-name.component.html',
styleUrls : ['./edit-board-name.component.scss'] styleUrls : ['./edit-board-name.component.scss']
}) })
export class FuseScrumboardEditBoardNameComponent export class ScrumboardEditBoardNameComponent
{ {
formActive = false; formActive: boolean;
form: FormGroup; form: FormGroup;
@Input() board;
@Output() onNameChanged = new EventEmitter(); @Input()
@ViewChild('nameInput') nameInputField; board;
@Output()
onNameChanged: EventEmitter<any>;
@ViewChild('nameInput')
nameInputField;
constructor( constructor(
private formBuilder: FormBuilder private formBuilder: FormBuilder
) )
{ {
// Set the defaults
this.formActive = false;
this.onNameChanged = new EventEmitter();
} }
openForm() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Open form
*/
openForm(): void
{ {
this.form = this.formBuilder.group({ this.form = this.formBuilder.group({
name: [this.board.name] name: [this.board.name]
@ -29,19 +45,28 @@ export class FuseScrumboardEditBoardNameComponent
this.focusNameField(); this.focusNameField();
} }
closeForm() /**
* Close form
*/
closeForm(): void
{ {
this.formActive = false; this.formActive = false;
} }
focusNameField() /**
* Focus to the name field
*/
focusNameField(): void
{ {
setTimeout(() => { setTimeout(() => {
this.nameInputField.nativeElement.focus(); this.nameInputField.nativeElement.focus();
}); });
} }
onFormSubmit() /**
* On form submit
*/
onFormSubmit(): void
{ {
if ( this.form.valid ) if ( this.form.valid )
{ {
@ -52,5 +77,4 @@ export class FuseScrumboardEditBoardNameComponent
this.formActive = false; this.formActive = false;
} }
} }
} }

View File

@ -2,45 +2,73 @@ import { Component, EventEmitter, Output, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
@Component({ @Component({
selector : 'fuse-scrumboard-board-add-card', selector : 'scrumboard-board-add-card',
templateUrl: './add-card.component.html', templateUrl: './add-card.component.html',
styleUrls : ['./add-card.component.scss'] styleUrls : ['./add-card.component.scss']
}) })
export class FuseScrumboardBoardAddCardComponent export class ScrumboardBoardAddCardComponent
{ {
formActive = false; formActive: boolean;
form: FormGroup; form: FormGroup;
@Output() onCardAdd = new EventEmitter();
@ViewChild('nameInput') nameInputField;
@Output()
onCardAdd: EventEmitter<any>;
@ViewChild('nameInput')
nameInputField;
/**
* Constructor
*
* @param {FormBuilder} _formBuilder
*/
constructor( constructor(
private formBuilder: FormBuilder private _formBuilder: FormBuilder
) )
{ {
// Set the defaults
this.formActive = false;
this.onCardAdd = new EventEmitter();
} }
openForm() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Open the form
*/
openForm(): void
{ {
this.form = this.formBuilder.group({ this.form = this._formBuilder.group({
name: '' name: ''
}); });
this.formActive = true; this.formActive = true;
this.focusNameField(); this.focusNameField();
} }
closeForm() /**
* Close the form
*/
closeForm(): void
{ {
this.formActive = false; this.formActive = false;
} }
focusNameField() /**
* Focus to the name field
*/
focusNameField(): void
{ {
setTimeout(() => { setTimeout(() => {
this.nameInputField.nativeElement.focus(); this.nameInputField.nativeElement.focus();
}); });
} }
onFormSubmit() /**
* On form submit
*/
onFormSubmit(): void
{ {
if ( this.form.valid ) if ( this.form.valid )
{ {

View File

@ -3,40 +3,57 @@ import { ActivatedRoute } from '@angular/router';
import * as moment from 'moment'; import * as moment from 'moment';
@Component({ @Component({
selector : 'fuse-scrumboard-board-card', selector : 'scrumboard-board-card',
templateUrl : './card.component.html', templateUrl : './card.component.html',
styleUrls : ['./card.component.scss'], styleUrls : ['./card.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class FuseScrumboardBoardCardComponent implements OnInit export class ScrumboardBoardCardComponent implements OnInit
{ {
@Input() cardId; @Input()
cardId;
card: any; card: any;
board: any; board: any;
/**
* Constructor
*
* @param {ActivatedRoute} _activatedRoute
*/
constructor( constructor(
private route: ActivatedRoute, private _activatedRoute: ActivatedRoute
) )
{ {
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.board = this.route.snapshot.data.board; this.board = this._activatedRoute.snapshot.data.board;
this.card = this.board.cards.filter((card) => { this.card = this.board.cards.filter((card) => {
return this.cardId === card.id; return this.cardId === card.id;
})[0]; })[0];
} }
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/** /**
* Is the card overdue? * Is the card overdue?
* *
* @param cardDate * @param cardDate
* @returns {boolean} * @returns {boolean}
*/ */
isOverdue(cardDate) isOverdue(cardDate): boolean
{ {
return moment() > moment(new Date(cardDate)); return moment() > moment(new Date(cardDate));
} }
} }

View File

@ -2,46 +2,76 @@ import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
@Component({ @Component({
selector : 'fuse-scrumboard-board-edit-list-name', selector : 'scrumboard-board-edit-list-name',
templateUrl: './edit-list-name.component.html', templateUrl: './edit-list-name.component.html',
styleUrls : ['./edit-list-name.component.scss'] styleUrls : ['./edit-list-name.component.scss']
}) })
export class FuseScrumboardBoardEditListNameComponent export class ScrumboardBoardEditListNameComponent
{ {
formActive = false; formActive: boolean;
form: FormGroup; form: FormGroup;
@Input() list;
@Output() onNameChanged = new EventEmitter();
@ViewChild('nameInput') nameInputField;
@Input()
list;
@Output()
onNameChanged: EventEmitter<any>;
@ViewChild('nameInput')
nameInputField;
/**
* Constructor
*
* @param {FormBuilder} _formBuilder
*/
constructor( constructor(
private formBuilder: FormBuilder private _formBuilder: FormBuilder
) )
{ {
// Set the defaults
this.formActive = false;
this.onNameChanged = new EventEmitter();
} }
openForm() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Open the form
*/
openForm(): void
{ {
this.form = this.formBuilder.group({ this.form = this._formBuilder.group({
name: [this.list.name] name: [this.list.name]
}); });
this.formActive = true; this.formActive = true;
this.focusNameField(); this.focusNameField();
} }
closeForm() /**
* Close the form
*/
closeForm(): void
{ {
this.formActive = false; this.formActive = false;
} }
focusNameField() /**
* Focus to the name field
*/
focusNameField(): void
{ {
setTimeout(() => { setTimeout(() => {
this.nameInputField.nativeElement.focus(); this.nameInputField.nativeElement.focus();
}); });
} }
onFormSubmit() /**
* On form submit
*/
onFormSubmit(): void
{ {
if ( this.form.valid ) if ( this.form.valid )
{ {
@ -50,5 +80,4 @@ export class FuseScrumboardBoardEditListNameComponent
this.formActive = false; this.formActive = false;
} }
} }
} }

View File

@ -3,11 +3,11 @@
<!-- LIST HEADER --> <!-- LIST HEADER -->
<div class="list-header" fxFlex fxLayout="row" fxLayoutAlign="space-between center"> <div class="list-header" fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<fuse-scrumboard-board-edit-list-name <scrumboard-board-edit-list-name
fxFlex="1 0 auto" fxFlex="1 0 auto"
[list]="list" [list]="list"
(onNameChanged)="onListNameChanged($event)"> (onNameChanged)="onListNameChanged($event)">
</fuse-scrumboard-board-edit-list-name> </scrumboard-board-edit-list-name>
<div fxFlex="0 1 auto"> <div fxFlex="0 1 auto">
<button mat-icon-button class="list-header-option-button" [matMenuTriggerFor]="listMenu"> <button mat-icon-button class="list-header-option-button" [matMenuTriggerFor]="listMenu">
@ -27,21 +27,21 @@
<div class="list-cards ngx-dnd-container" <div class="list-cards ngx-dnd-container"
[model]="list.idCards" ngxDroppable="card" (out)="onDrop($event)" [model]="list.idCards" ngxDroppable="card" (out)="onDrop($event)"
fusePerfectScrollbar #listScroll> fusePerfectScrollbar #listScroll>
<fuse-scrumboard-board-card ngxDraggable <scrumboard-board-card ngxDraggable
(click)="openCardDialog(cardId)" (click)="openCardDialog(cardId)"
class="scrumboard-board-card mat-elevation-z2 ngx-dnd-item" class="scrumboard-board-card mat-elevation-z2 ngx-dnd-item"
*ngFor="let cardId of list.idCards" *ngFor="let cardId of list.idCards"
[model]="cardId" [model]="cardId"
[cardId]="cardId"> [cardId]="cardId">
</fuse-scrumboard-board-card> </scrumboard-board-card>
</div> </div>
</div> </div>
<!-- / LIST CONTENT --> <!-- / LIST CONTENT -->
<!-- NEW CARD BUTTON--> <!-- NEW CARD BUTTON-->
<div class="list-footer"> <div class="list-footer">
<fuse-scrumboard-board-add-card (onCardAdd)="onCardAdd($event)"> <scrumboard-board-add-card (onCardAdd)="onCardAdd($event)">
</fuse-scrumboard-board-add-card> </scrumboard-board-add-card>
</div> </div>
<!-- / NEW CARD BUTTON--> <!-- / NEW CARD BUTTON-->

View File

@ -1,78 +1,122 @@
import { Component, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { Component, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { MatDialog, MatDialogRef } from '@angular/material'; import { MatDialog, MatDialogRef } from '@angular/material';
import { Subject } from 'rxjs';
import { Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators';
import { FuseConfirmDialogComponent } from '@fuse/components/confirm-dialog/confirm-dialog.component'; import { FuseConfirmDialogComponent } from '@fuse/components/confirm-dialog/confirm-dialog.component';
import { FusePerfectScrollbarDirective } from '@fuse/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.directive'; import { FusePerfectScrollbarDirective } from '@fuse/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.directive';
import { Card } from '../../card.model'; import { ScrumboardService } from 'app/main/apps/scrumboard/scrumboard.service';
import { ScrumboardService } from '../../scrumboard.service'; import { Card } from 'app/main/apps/scrumboard/card.model';
import { FuseScrumboardCardDialogComponent } from '../dialogs/card/card.component'; import { ScrumboardCardDialogComponent } from 'app/main/apps/scrumboard/board/dialogs/card/card.component';
@Component({ @Component({
selector : 'fuse-scrumboard-board-list', selector : 'scrumboard-board-list',
templateUrl : './list.component.html', templateUrl : './list.component.html',
styleUrls : ['./list.component.scss'], styleUrls : ['./list.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class FuseScrumboardBoardListComponent implements OnInit, OnDestroy export class ScrumboardBoardListComponent implements OnInit, OnDestroy
{ {
board: any; board: any;
dialogRef: any; dialogRef: any;
@Input() list; @Input()
@ViewChild(FusePerfectScrollbarDirective) listScroll: FusePerfectScrollbarDirective; list;
@ViewChild(FusePerfectScrollbarDirective)
listScroll: FusePerfectScrollbarDirective;
onBoardChanged: Subscription;
confirmDialogRef: MatDialogRef<FuseConfirmDialogComponent>; confirmDialogRef: MatDialogRef<FuseConfirmDialogComponent>;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {ActivatedRoute} _activatedRoute
* @param {ScrumboardService} _scrumboardService
* @param {MatDialog} _matDialog
*/
constructor( constructor(
private route: ActivatedRoute, private _activatedRoute: ActivatedRoute,
private scrumboardService: ScrumboardService, private _scrumboardService: ScrumboardService,
public dialog: MatDialog private _matDialog: MatDialog
) )
{ {
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onBoardChanged = this._scrumboardService.onBoardChanged
this.scrumboardService.onBoardChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(board => { .subscribe(board => {
this.board = board; this.board = board;
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onBoardChanged.unsubscribe(); // Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
onListNameChanged(newListName) // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* On list name changed
*
* @param newListName
*/
onListNameChanged(newListName): void
{ {
this.list.name = newListName; this.list.name = newListName;
} }
onCardAdd(newCardName) /**
* On card added
*
* @param newCardName
*/
onCardAdd(newCardName): void
{ {
if ( newCardName === '' ) if ( newCardName === '' )
{ {
return; return;
} }
this.scrumboardService.addCard(this.list.id, new Card({name: newCardName})); this._scrumboardService.addCard(this.list.id, new Card({name: newCardName}));
setTimeout(() => { setTimeout(() => {
this.listScroll.scrollToBottom(0, 400); this.listScroll.scrollToBottom(0, 400);
}); });
} }
removeList(listId) /**
* Remove list
*
* @param listId
*/
removeList(listId): void
{ {
this.confirmDialogRef = this.dialog.open(FuseConfirmDialogComponent, { this.confirmDialogRef = this._matDialog.open(FuseConfirmDialogComponent, {
disableClose: false disableClose: false
}); });
@ -81,14 +125,19 @@ export class FuseScrumboardBoardListComponent implements OnInit, OnDestroy
this.confirmDialogRef.afterClosed().subscribe(result => { this.confirmDialogRef.afterClosed().subscribe(result => {
if ( result ) if ( result )
{ {
this.scrumboardService.removeList(listId); this._scrumboardService.removeList(listId);
} }
}); });
} }
openCardDialog(cardId) /**
* Open card dialog
*
* @param cardId
*/
openCardDialog(cardId): void
{ {
this.dialogRef = this.dialog.open(FuseScrumboardCardDialogComponent, { this.dialogRef = this._matDialog.open(ScrumboardCardDialogComponent, {
panelClass: 'scrumboard-card-dialog', panelClass: 'scrumboard-card-dialog',
data : { data : {
cardId: cardId, cardId: cardId,
@ -101,8 +150,13 @@ export class FuseScrumboardBoardListComponent implements OnInit, OnDestroy
}); });
} }
onDrop(ev) /**
* On drop
*
* @param ev
*/
onDrop(ev): void
{ {
this.scrumboardService.updateBoard(); this._scrumboardService.updateBoard();
} }
} }

View File

@ -1,46 +1,78 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators';
import { MatColors } from '@fuse/mat-colors'; import { MatColors } from '@fuse/mat-colors';
import { ScrumboardService } from '../../../../scrumboard.service'; import { ScrumboardService } from 'app/main/apps/scrumboard/scrumboard.service';
@Component({ @Component({
selector : 'fuse-scrumboard-board-color-selector', selector : 'scrumboard-board-color-selector',
templateUrl: './board-color-selector.component.html', templateUrl: './board-color-selector.component.html',
styleUrls : ['./board-color-selector.component.scss'] styleUrls : ['./board-color-selector.component.scss']
}) })
export class FuseScrumboardBoardColorSelectorComponent implements OnInit, OnDestroy export class ScrumboardBoardColorSelectorComponent implements OnInit, OnDestroy
{ {
colors: any; colors: any;
board: any; board: any;
onBoardChanged: Subscription;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {ScrumboardService} _scrumboardService
*/
constructor( constructor(
private scrumboardService: ScrumboardService private _scrumboardService: ScrumboardService
) )
{ {
// Set the defaults
this.colors = MatColors.all; this.colors = MatColors.all;
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onBoardChanged = this._scrumboardService.onBoardChanged
this.scrumboardService.onBoardChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(board => { .subscribe(board => {
this.board = board; this.board = board;
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onBoardChanged.unsubscribe(); // Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
setColor(color) // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Set the color
*
* @param color
*/
setColor(color): void
{ {
this.board.settings.color = color; this.board.settings.color = color;
this.scrumboardService.updateBoard(); this._scrumboardService.updateBoard();
} }
} }

View File

@ -28,7 +28,7 @@
</div> </div>
<div class="nav-item"> <div class="nav-item">
<div class="nav-link" matRipple (click)="toggleSubcription()"> <div class="nav-link" matRipple (click)="toggleSubscription()">
<mat-icon class="nav-link-icon">remove_red_eye</mat-icon> <mat-icon class="nav-link-icon">remove_red_eye</mat-icon>
<p fxFlex class="title">Subscribe</p> <p fxFlex class="title">Subscribe</p>
<mat-icon *ngIf="board.settings.subscribed" class="s-18">check</mat-icon> <mat-icon *ngIf="board.settings.subscribed" class="s-18">check</mat-icon>
@ -68,7 +68,7 @@
<!-- SIDENAV CONTENT --> <!-- SIDENAV CONTENT -->
<div class="content p-8" fusePerfectScrollbar> <div class="content p-8" fusePerfectScrollbar>
<fuse-scrumboard-board-color-selector></fuse-scrumboard-board-color-selector> <scrumboard-board-color-selector></scrumboard-board-color-selector>
</div> </div>
<!-- / SIDENAV CONTENT --> <!-- / SIDENAV CONTENT -->

View File

@ -1,50 +1,78 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { ScrumboardService } from 'app/main/apps/scrumboard/scrumboard.service';
import { ScrumboardService } from '../../../scrumboard.service'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector : 'fuse-scrumboard-board-settings', selector : 'scrumboard-board-settings',
templateUrl: './settings.component.html', templateUrl: './settings.component.html',
styleUrls : ['./settings.component.scss'], styleUrls : ['./settings.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseScrumboardBoardSettingsSidenavComponent implements OnInit, OnDestroy export class ScrumboardBoardSettingsSidenavComponent implements OnInit, OnDestroy
{ {
board: any; board: any;
view = 'main'; view: string;
onBoardChanged: Subscription;
// Private
private _unsubscribeAll: Subject<any>;
constructor( constructor(
private scrumboardService: ScrumboardService private scrumboardService: ScrumboardService
) )
{ {
// Set the defaults
this.view = 'main';
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onBoardChanged = this.scrumboardService.onBoardChanged
this.scrumboardService.onBoardChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(board => { .subscribe(board => {
this.board = board; this.board = board;
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onBoardChanged.unsubscribe(); // Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
toggleCardCover() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Toggle card cover
*/
toggleCardCover(): void
{ {
this.board.settings.cardCoverImages = !this.board.settings.cardCoverImages; this.board.settings.cardCoverImages = !this.board.settings.cardCoverImages;
this.scrumboardService.updateBoard(); this.scrumboardService.updateBoard();
} }
toggleSubcription() /**
* Toggle subscription
*/
toggleSubscription(): void
{ {
this.board.settings.subscribed = !this.board.settings.subscribed; this.board.settings.subscribed = !this.board.settings.subscribed;
this.scrumboardService.updateBoard(); this.scrumboardService.updateBoard();

View File

@ -17,6 +17,11 @@ export class Card
activities: any[]; activities: any[];
due: string; due: string;
/**
* Constructor
*
* @param card
*/
constructor(card) constructor(card)
{ {
this.id = card.id || FuseUtils.generateGUID(); this.id = card.id || FuseUtils.generateGUID();

View File

@ -6,6 +6,11 @@ export class List
name: string; name: string;
idCards: string[]; idCards: string[];
/**
* Constructor
*
* @param list
*/
constructor(list) constructor(list)
{ {
this.id = list.id || FuseUtils.generateGUID(); this.id = list.id || FuseUtils.generateGUID();

View File

@ -1,50 +1,79 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Subject } from 'rxjs';
import { Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { Board } from './board.model'; import { ScrumboardService } from 'app/main/apps/scrumboard/scrumboard.service';
import { ScrumboardService } from './scrumboard.service'; import { Board } from 'app/main/apps/scrumboard/board.model';
@Component({ @Component({
selector : 'fuse-scrumboard', selector : 'scrumboard',
templateUrl: './scrumboard.component.html', templateUrl: './scrumboard.component.html',
styleUrls : ['./scrumboard.component.scss'], styleUrls : ['./scrumboard.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseScrumboardComponent implements OnInit, OnDestroy export class ScrumboardComponent implements OnInit, OnDestroy
{ {
boards: any[]; boards: any[];
onBoardsChanged: Subscription;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {Router} _router
* @param {ScrumboardService} _scrumboardService
*/
constructor( constructor(
private router: Router, private _router: Router,
private scrumboardService: ScrumboardService private _scrumboardService: ScrumboardService
) )
{ {
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onBoardsChanged = this._scrumboardService.onBoardsChanged
this.scrumboardService.onBoardsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(boards => { .subscribe(boards => {
this.boards = boards; this.boards = boards;
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onBoardsChanged.unsubscribe(); // Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
newBoard() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* New board
*/
newBoard(): void
{ {
const newBoard = new Board({}); const newBoard = new Board({});
this.scrumboardService.createNewBoard(newBoard).then(() => { this._scrumboardService.createNewBoard(newBoard).then(() => {
this.router.navigate(['/apps/scrumboard/boards/' + newBoard.id + '/' + newBoard.uri]); this._router.navigate(['/apps/scrumboard/boards/' + newBoard.id + '/' + newBoard.uri]);
}); });
} }
} }

View File

@ -1,37 +1,36 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { MatButtonModule, MatCheckboxModule, MatChipsModule, MatDatepickerModule, MatDialogModule, MatFormFieldModule, MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatProgressBarModule, MatRippleModule, MatSidenavModule, MatToolbarModule, MatTooltipModule } from '@angular/material'; import { MatButtonModule, MatCheckboxModule, MatChipsModule, MatDatepickerModule, MatDialogModule, MatFormFieldModule, MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatProgressBarModule, MatRippleModule, MatSidenavModule, MatToolbarModule, MatTooltipModule } from '@angular/material';
import { NgxDnDModule } from '@swimlane/ngx-dnd';
import { FuseSharedModule } from '@fuse/shared.module'; import { FuseSharedModule } from '@fuse/shared.module';
import { FuseConfirmDialogModule, FuseMaterialColorPickerModule } from '@fuse/components'; import { FuseConfirmDialogModule, FuseMaterialColorPickerModule } from '@fuse/components';
import { BoardResolve, ScrumboardService } from './scrumboard.service'; import { BoardResolve, ScrumboardService } from 'app/main/apps/scrumboard/scrumboard.service';
import { FuseScrumboardComponent } from './scrumboard.component'; import { ScrumboardComponent } from 'app/main/apps/scrumboard/scrumboard.component';
import { FuseScrumboardBoardComponent } from './board/board.component'; import { ScrumboardBoardComponent } from 'app/main/apps/scrumboard/board/board.component';
import { FuseScrumboardBoardListComponent } from './board/list/list.component'; import { ScrumboardBoardListComponent } from 'app/main/apps/scrumboard/board/list/list.component';
import { FuseScrumboardBoardCardComponent } from './board/list/card/card.component'; import { ScrumboardBoardCardComponent } from 'app/main/apps/scrumboard/board/list/card/card.component';
import { FuseScrumboardBoardEditListNameComponent } from './board/list/edit-list-name/edit-list-name.component'; import { ScrumboardBoardEditListNameComponent } from 'app/main/apps/scrumboard/board/list/edit-list-name/edit-list-name.component';
import { FuseScrumboardBoardAddCardComponent } from './board/list/add-card/add-card.component'; import { ScrumboardBoardAddCardComponent } from 'app/main/apps/scrumboard/board/list/add-card/add-card.component';
import { FuseScrumboardBoardAddListComponent } from './board/add-list/add-list.component'; import { ScrumboardBoardAddListComponent } from 'app/main/apps/scrumboard/board/add-list/add-list.component';
import { FuseScrumboardCardDialogComponent } from './board/dialogs/card/card.component'; import { ScrumboardCardDialogComponent } from 'app/main/apps/scrumboard/board/dialogs/card/card.component';
import { FuseScrumboardLabelSelectorComponent } from './board/dialogs/card/label-selector/label-selector.component'; import { ScrumboardLabelSelectorComponent } from 'app/main/apps/scrumboard/board/dialogs/card/label-selector/label-selector.component';
import { FuseScrumboardEditBoardNameComponent } from './board/edit-board-name/edit-board-name.component'; import { ScrumboardEditBoardNameComponent } from 'app/main/apps/scrumboard/board/edit-board-name/edit-board-name.component';
import { FuseScrumboardBoardSettingsSidenavComponent } from './board/sidenavs/settings/settings.component'; import { ScrumboardBoardSettingsSidenavComponent } from 'app/main/apps/scrumboard/board/sidenavs/settings/settings.component';
import { FuseScrumboardBoardColorSelectorComponent } from './board/sidenavs/settings/board-color-selector/board-color-selector.component'; import { ScrumboardBoardColorSelectorComponent } from 'app/main/apps/scrumboard/board/sidenavs/settings/board-color-selector/board-color-selector.component';
import { NgxDnDModule } from '@swimlane/ngx-dnd';
const routes: Routes = [ const routes: Routes = [
{ {
path : 'boards', path : 'boards',
component: FuseScrumboardComponent, component: ScrumboardComponent,
resolve : { resolve : {
scrumboard: ScrumboardService scrumboard: ScrumboardService
} }
}, },
{ {
path : 'boards/:boardId/:boardUri', path : 'boards/:boardId/:boardUri',
component: FuseScrumboardBoardComponent, component: ScrumboardBoardComponent,
resolve : { resolve : {
board: BoardResolve board: BoardResolve
} }
@ -44,18 +43,18 @@ const routes: Routes = [
@NgModule({ @NgModule({
declarations : [ declarations : [
FuseScrumboardComponent, ScrumboardComponent,
FuseScrumboardBoardComponent, ScrumboardBoardComponent,
FuseScrumboardBoardListComponent, ScrumboardBoardListComponent,
FuseScrumboardBoardCardComponent, ScrumboardBoardCardComponent,
FuseScrumboardBoardEditListNameComponent, ScrumboardBoardEditListNameComponent,
FuseScrumboardBoardAddCardComponent, ScrumboardBoardAddCardComponent,
FuseScrumboardBoardAddListComponent, ScrumboardBoardAddListComponent,
FuseScrumboardCardDialogComponent, ScrumboardCardDialogComponent,
FuseScrumboardLabelSelectorComponent, ScrumboardLabelSelectorComponent,
FuseScrumboardEditBoardNameComponent, ScrumboardEditBoardNameComponent,
FuseScrumboardBoardSettingsSidenavComponent, ScrumboardBoardSettingsSidenavComponent,
FuseScrumboardBoardColorSelectorComponent ScrumboardBoardColorSelectorComponent
], ],
imports : [ imports : [
RouterModule.forChild(routes), RouterModule.forChild(routes),
@ -86,7 +85,7 @@ const routes: Routes = [
ScrumboardService, ScrumboardService,
BoardResolve BoardResolve
], ],
entryComponents: [FuseScrumboardCardDialogComponent] entryComponents: [ScrumboardCardDialogComponent]
}) })
export class FuseScrumboardModule export class FuseScrumboardModule
{ {

View File

@ -1,7 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable, BehaviorSubject } from 'rxjs'; import { Observable, BehaviorSubject } from 'rxjs';
@Injectable() @Injectable()
@ -11,15 +10,26 @@ export class ScrumboardService implements Resolve<any>
routeParams: any; routeParams: any;
board: any; board: any;
onBoardsChanged: BehaviorSubject<any> = new BehaviorSubject([]); onBoardsChanged: BehaviorSubject<any>;
onBoardChanged: BehaviorSubject<any> = new BehaviorSubject([]); onBoardChanged: BehaviorSubject<any>;
constructor(private http: HttpClient) /**
* Constructor
*
* @param {HttpClient} _httpClient
*/
constructor(
private _httpClient: HttpClient
)
{ {
// Set the defaults
this.onBoardsChanged = new BehaviorSubject([]);
this.onBoardChanged = new BehaviorSubject([]);
} }
/** /**
* Resolve * Resolver
*
* @param {ActivatedRouteSnapshot} route * @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state * @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any} * @returns {Observable<any> | Promise<any> | any}
@ -40,10 +50,15 @@ export class ScrumboardService implements Resolve<any>
}); });
} }
/**
* Get boards
*
* @returns {Promise<any>}
*/
getBoards(): Promise<any> getBoards(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/scrumboard-boards') this._httpClient.get('api/scrumboard-boards')
.subscribe((response: any) => { .subscribe((response: any) => {
this.boards = response; this.boards = response;
this.onBoardsChanged.next(this.boards); this.onBoardsChanged.next(this.boards);
@ -52,10 +67,16 @@ export class ScrumboardService implements Resolve<any>
}); });
} }
/**
* Get board
*
* @param boardId
* @returns {Promise<any>}
*/
getBoard(boardId): Promise<any> getBoard(boardId): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/scrumboard-boards/' + boardId) this._httpClient.get('api/scrumboard-boards/' + boardId)
.subscribe((response: any) => { .subscribe((response: any) => {
this.board = response; this.board = response;
this.onBoardChanged.next(this.board); this.onBoardChanged.next(this.board);
@ -64,7 +85,14 @@ export class ScrumboardService implements Resolve<any>
}); });
} }
addCard(listId, newCard) /**
* Add card
*
* @param listId
* @param newCard
* @returns {Promise<any>}
*/
addCard(listId, newCard): Promise<any>
{ {
this.board.lists.map((list) => { this.board.lists.map((list) => {
if ( list.id === listId ) if ( list.id === listId )
@ -78,16 +106,26 @@ export class ScrumboardService implements Resolve<any>
return this.updateBoard(); return this.updateBoard();
} }
addList(newList) /**
* Add list
*
* @param newList
* @returns {Promise<any>}
*/
addList(newList): Promise<any>
{ {
this.board.lists.push(newList); this.board.lists.push(newList);
return this.updateBoard(); return this.updateBoard();
} }
removeList(listId) /**
* Remove list
*
* @param listId
* @returns {Promise<any>}
*/
removeList(listId): Promise<any>
{ {
const list = this.board.lists.find((_list) => { const list = this.board.lists.find((_list) => {
return _list.id === listId; return _list.id === listId;
@ -105,9 +143,14 @@ export class ScrumboardService implements Resolve<any>
return this.updateBoard(); return this.updateBoard();
} }
removeCard(cardId, listId?) /**
* Remove card
*
* @param cardId
* @param listId
*/
removeCard(cardId, listId?): void
{ {
const card = this.board.cards.find((_card) => { const card = this.board.cards.find((_card) => {
return _card.id === cardId; return _card.id === cardId;
}); });
@ -125,10 +168,15 @@ export class ScrumboardService implements Resolve<any>
this.updateBoard(); this.updateBoard();
} }
updateBoard() /**
* Update board
*
* @returns {Promise<any>}
*/
updateBoard(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.post('api/scrumboard-boards/' + this.board.id, this.board) this._httpClient.post('api/scrumboard-boards/' + this.board.id, this.board)
.subscribe(response => { .subscribe(response => {
this.onBoardChanged.next(this.board); this.onBoardChanged.next(this.board);
resolve(this.board); resolve(this.board);
@ -136,7 +184,12 @@ export class ScrumboardService implements Resolve<any>
}); });
} }
updateCard(newCard) /**
* Update card
*
* @param newCard
*/
updateCard(newCard): void
{ {
this.board.cards.map((_card) => { this.board.cards.map((_card) => {
if ( _card.id === newCard.id ) if ( _card.id === newCard.id )
@ -148,10 +201,16 @@ export class ScrumboardService implements Resolve<any>
this.updateBoard(); this.updateBoard();
} }
createNewBoard(board) /**
* Create new board
*
* @param board
* @returns {Promise<any>}
*/
createNewBoard(board): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.post('api/scrumboard-boards/' + board.id, board) this._httpClient.post('api/scrumboard-boards/' + board.id, board)
.subscribe(response => { .subscribe(response => {
resolve(board); resolve(board);
}, reject); }, reject);
@ -162,12 +221,25 @@ export class ScrumboardService implements Resolve<any>
@Injectable() @Injectable()
export class BoardResolve implements Resolve<any> export class BoardResolve implements Resolve<any>
{ {
constructor(private scrumboardService: ScrumboardService) /**
* Constructor
*
* @param {ScrumboardService} _scrumboardService
*/
constructor(
private _scrumboardService: ScrumboardService
)
{ {
} }
resolve(route: ActivatedRouteSnapshot) /**
* Resolver
*
* @param {ActivatedRouteSnapshot} route
* @returns {Promise<any>}
*/
resolve(route: ActivatedRouteSnapshot): Promise<any>
{ {
return this.scrumboardService.getBoard(route.paramMap.get('boardId')); return this._scrumboardService.getBoard(route.paramMap.get('boardId'));
} }
} }

View File

@ -1,18 +1,19 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { TodoService } from '../../todo.service'; import { TodoService } from 'app/main/apps/todo/todo.service';
@Component({ @Component({
selector : 'fuse-todo-main-sidenav', selector : 'todo-main-sidenav',
templateUrl: './main-sidenav.component.html', templateUrl: './main-sidenav.component.html',
styleUrls : ['./main-sidenav.component.scss'], styleUrls : ['./main-sidenav.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseTodoMainSidenavComponent implements OnInit, OnDestroy export class TodoMainSidenavComponent implements OnInit, OnDestroy
{ {
folders: any[]; folders: any[];
filters: any[]; filters: any[];
@ -20,46 +21,75 @@ export class FuseTodoMainSidenavComponent implements OnInit, OnDestroy
accounts: object; accounts: object;
selectedAccount: string; selectedAccount: string;
onFiltersChanged: Subscription; // Private
onTagsChanged: Subscription; private _unsubscribeAll: Subject<any>;
constructor(private todoService: TodoService, private router: Router) /**
* Constructor
*
* @param {TodoService} _todoService
* @param {Router} _router
*/
constructor(
private _todoService: TodoService,
private _router: Router
)
{ {
// Data // Set the defaults
this.accounts = { this.accounts = {
'creapond' : 'johndoe@creapond.com', 'creapond' : 'johndoe@creapond.com',
'withinpixels': 'johndoe@withinpixels.com' 'withinpixels': 'johndoe@withinpixels.com'
}; };
this.selectedAccount = 'creapond'; this.selectedAccount = 'creapond';
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
{ // @ Lifecycle hooks
this.onFiltersChanged = // -----------------------------------------------------------------------------------------------------
this.todoService.onFiltersChanged
.subscribe(filters => {
this.filters = filters;
});
this.onTagsChanged = /**
this.todoService.onTagsChanged * On init
.subscribe(tags => { */
this.tags = tags; ngOnInit(): void
}); {
this._todoService.onFiltersChanged
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(filters => {
this.filters = filters;
});
this._todoService.onTagsChanged
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(tags => {
this.tags = tags;
});
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onFiltersChanged.unsubscribe(); // Unsubscribe from all subscriptions
this.onTagsChanged.unsubscribe(); this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
newTodo() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* New todo
*/
newTodo(): void
{ {
this.router.navigate(['/apps/todo/all']).then(() => { this._router.navigate(['/apps/todo/all']).then(() => {
setTimeout(() => { setTimeout(() => {
this.todoService.onNewTodoClicked.next(''); this._todoService.onNewTodoClicked.next('');
}); });
}); });
} }

View File

@ -1,108 +1,132 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { Subject } from 'rxjs';
import { Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { FuseUtils } from '@fuse/utils'; import { FuseUtils } from '@fuse/utils';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { Todo } from '../todo.model'; import { Todo } from 'app/main/apps/todo/todo.model';
import { TodoService } from '../todo.service'; import { TodoService } from 'app/main/apps/todo/todo.service';
@Component({ @Component({
selector : 'fuse-todo-details', selector : 'todo-details',
templateUrl: './todo-details.component.html', templateUrl: './todo-details.component.html',
styleUrls : ['./todo-details.component.scss'], styleUrls : ['./todo-details.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseTodoDetailsComponent implements OnInit, OnDestroy export class TodoDetailsComponent implements OnInit, OnDestroy
{ {
todo: Todo; todo: Todo;
tags: any[]; tags: any[];
formType: string; formType: string;
todoForm: FormGroup; todoForm: FormGroup;
@ViewChild('titleInput') titleInputField;
onFormChange: any; @ViewChild('titleInput')
onCurrentTodoChanged: Subscription; titleInputField;
onTagsChanged: Subscription;
onNewTodoClicked: Subscription;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {TodoService} _todoService
* @param {FormBuilder} _formBuilder
*/
constructor( constructor(
private todoService: TodoService, private _todoService: TodoService,
private formBuilder: FormBuilder private _formBuilder: FormBuilder
) )
{ {
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
// Subscribe to update the current todo // Subscribe to update the current todo
this.onCurrentTodoChanged = this._todoService.onCurrentTodoChanged
this.todoService.onCurrentTodoChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(([todo, formType]) => { .subscribe(([todo, formType]) => {
if ( todo && formType === 'edit' ) if ( todo && formType === 'edit' )
{ {
this.formType = 'edit'; this.formType = 'edit';
this.todo = todo;
this.todo = todo;
this.todoForm = this.createTodoForm();
this.onFormChange =
this.todoForm.valueChanges.pipe(
debounceTime(500),
distinctUntilChanged()
).subscribe(data => {
this.todoService.updateTodo(data);
});
}
});
// Subscribe to update on tag change
this.onTagsChanged =
this.todoService.onTagsChanged
.subscribe(labels => {
this.tags = labels;
});
// Subscribe to update on tag change
this.onNewTodoClicked =
this.todoService.onNewTodoClicked
.subscribe(() => {
this.todo = new Todo({});
this.todo.id = FuseUtils.generateGUID();
this.formType = 'new';
this.todoForm = this.createTodoForm(); this.todoForm = this.createTodoForm();
this.focusTitleField();
this.todoService.onCurrentTodoChanged.next([this.todo, 'new']); this.todoForm.valueChanges
}); .pipe(
takeUntil(this._unsubscribeAll),
debounceTime(500),
distinctUntilChanged()
)
.subscribe(data => {
this._todoService.updateTodo(data);
});
}
});
// Subscribe to update on tag change
this._todoService.onTagsChanged
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(labels => {
this.tags = labels;
});
// Subscribe to update on tag change
this._todoService.onNewTodoClicked
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(() => {
this.todo = new Todo({});
this.todo.id = FuseUtils.generateGUID();
this.formType = 'new';
this.todoForm = this.createTodoForm();
this.focusTitleField();
this._todoService.onCurrentTodoChanged.next([this.todo, 'new']);
});
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
if ( this.onFormChange ) // Unsubscribe from all subscriptions
{ this._unsubscribeAll.next();
this.onFormChange.unsubscribe(); this._unsubscribeAll.complete();
}
this.onCurrentTodoChanged.unsubscribe();
this.onNewTodoClicked.unsubscribe();
} }
focusTitleField() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Focus title field
*/
focusTitleField(): void
{ {
setTimeout(() => { setTimeout(() => {
this.titleInputField.nativeElement.focus(); this.titleInputField.nativeElement.focus();
}); });
} }
createTodoForm() /**
* Create todo form
*
* @returns {FormGroup}
*/
createTodoForm(): FormGroup
{ {
return this.formBuilder.group({ return this._formBuilder.group({
'id' : [this.todo.id], 'id' : [this.todo.id],
'title' : [this.todo.title], 'title' : [this.todo.title],
'notes' : [this.todo.notes], 'notes' : [this.todo.notes],
@ -116,54 +140,80 @@ export class FuseTodoDetailsComponent implements OnInit, OnDestroy
}); });
} }
toggleStar(event) /**
* Toggle star
*
* @param event
*/
toggleStar(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.todo.toggleStar(); this.todo.toggleStar();
this.todoService.updateTodo(this.todo); this._todoService.updateTodo(this.todo);
} }
toggleImportant(event) /**
* Toggle important
*
* @param event
*/
toggleImportant(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.todo.toggleImportant(); this.todo.toggleImportant();
this.todoService.updateTodo(this.todo); this._todoService.updateTodo(this.todo);
} }
/** /**
* Toggle Completed * Toggle Completed
*
* @param event * @param event
*/ */
toggleCompleted(event) toggleCompleted(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.todo.toggleCompleted(); this.todo.toggleCompleted();
this.todoService.updateTodo(this.todo); this._todoService.updateTodo(this.todo);
} }
/** /**
* Toggle Deleted * Toggle Deleted
*
* @param event * @param event
*/ */
toggleDeleted(event) toggleDeleted(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.todo.toggleDeleted(); this.todo.toggleDeleted();
this.todoService.updateTodo(this.todo); this._todoService.updateTodo(this.todo);
} }
toggleTagOnTodo(tagId) /**
* Toggle tag on todo
*
* @param tagId
*/
toggleTagOnTodo(tagId): void
{ {
this.todoService.toggleTagOnTodo(tagId, this.todo); this._todoService.toggleTagOnTodo(tagId, this.todo);
} }
hasTag(tagId) /**
* Has tag?
*
* @param tagId
* @returns {any}
*/
hasTag(tagId): any
{ {
return this.todoService.hasTag(tagId, this.todo); return this._todoService.hasTag(tagId, this.todo);
} }
addTodo() /**
* Add todo
*/
addTodo(): void
{ {
this.todoService.updateTodo(this.todoForm.getRawValue()); this._todoService.updateTodo(this.todoForm.getRawValue());
} }
} }

View File

@ -1,112 +1,148 @@
import { Component, HostBinding, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, HostBinding, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
import { Todo } from 'app/main/apps/todo/todo.model';
import { Todo } from '../../todo.model'; import { TodoService } from 'app/main/apps/todo/todo.service';
import { TodoService } from '../../todo.service'; import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector : 'fuse-todo-list-item', selector : 'todo-list-item',
templateUrl : './todo-list-item.component.html', templateUrl : './todo-list-item.component.html',
styleUrls : ['./todo-list-item.component.scss'], styleUrls : ['./todo-list-item.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class FuseTodoListItemComponent implements OnInit, OnDestroy export class TodoListItemComponent implements OnInit, OnDestroy
{ {
@Input() todo: Todo;
tags: any[]; tags: any[];
@HostBinding('class.selected') selected: boolean;
@HostBinding('class.completed') completed: boolean;
@HostBinding('class.move-disabled') moveDisabled: boolean;
onSelectedTodosChanged: Subscription; @Input()
onTagsChanged: Subscription; todo: Todo;
@HostBinding('class.selected')
selected: boolean;
@HostBinding('class.completed')
completed: boolean;
@HostBinding('class.move-disabled')
moveDisabled: boolean;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {TodoService} _todoService
* @param {ActivatedRoute} _activatedRoute
*/
constructor( constructor(
private todoService: TodoService, private _todoService: TodoService,
private route: ActivatedRoute private _activatedRoute: ActivatedRoute
) )
{ {
// Disable move if path is not /all // Disable move if path is not /all
if ( route.snapshot.url[0].path !== 'all' ) if ( _activatedRoute.snapshot.url[0].path !== 'all' )
{ {
this.moveDisabled = true; this.moveDisabled = true;
} }
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
// Set the initial values // Set the initial values
this.todo = new Todo(this.todo); this.todo = new Todo(this.todo);
this.completed = this.todo.completed; this.completed = this.todo.completed;
// Subscribe to update on selected todo change // Subscribe to update on selected todo change
this.onSelectedTodosChanged = this._todoService.onSelectedTodosChanged
this.todoService.onSelectedTodosChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(selectedTodos => { .subscribe(selectedTodos => {
this.selected = false; this.selected = false;
if ( selectedTodos.length > 0 ) if ( selectedTodos.length > 0 )
{
for ( const todo of selectedTodos )
{ {
for ( const todo of selectedTodos ) if ( todo.id === this.todo.id )
{ {
if ( todo.id === this.todo.id ) this.selected = true;
{ break;
this.selected = true;
break;
}
} }
} }
}); }
});
// Subscribe to update on tag change // Subscribe to update on tag change
this.onTagsChanged = this._todoService.onTagsChanged
this.todoService.onTagsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(tags => { .subscribe(tags => {
this.tags = tags; this.tags = tags;
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onSelectedTodosChanged.unsubscribe(); // Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
onSelectedChange() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* On selected change
*/
onSelectedChange(): void
{ {
this.todoService.toggleSelectedTodo(this.todo.id); this._todoService.toggleSelectedTodo(this.todo.id);
} }
/** /**
* Toggle star * Toggle star
*/ */
toggleStar(event) toggleStar(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.todo.toggleStar(); this.todo.toggleStar();
this.todoService.updateTodo(this.todo); this._todoService.updateTodo(this.todo);
} }
/** /**
* Toggle Important * Toggle Important
*/ */
toggleImportant(event) toggleImportant(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.todo.toggleImportant(); this.todo.toggleImportant();
this.todoService.updateTodo(this.todo); this._todoService.updateTodo(this.todo);
} }
/** /**
* Toggle Completed * Toggle Completed
*/ */
toggleCompleted(event) toggleCompleted(event): void
{ {
event.stopPropagation(); event.stopPropagation();
this.todo.toggleCompleted(); this.todo.toggleCompleted();
this.todoService.updateTodo(this.todo); this._todoService.updateTodo(this.todo);
} }
} }

View File

@ -2,13 +2,13 @@
<span class="no-todos-text hint-text">There are no todos!</span> <span class="no-todos-text hint-text">There are no todos!</span>
</div> </div>
<div class="todo-list" ngxDroppable [model]="todos" (out)="onDrop($event)" *fuseIfOnDom [@animateStagger]="{value:'50'}"> <div class="todo-list" ngxDroppable [model]="todos" (out)="onDrop($event)" *fuseIfOnDom [@animateStagger]="{value:'50'}">
<fuse-todo-list-item class="todo-list-item has-handle" <todo-list-item class="todo-list-item has-handle"
*ngFor="let todo of todos" [todo]="todo" *ngFor="let todo of todos" [todo]="todo"
ngxDraggable ngxDraggable
[model]="todo" [model]="todo"
(click)="readTodo(todo.id)" (click)="readTodo(todo.id)"
[ngClass]="{'current-todo':todo?.id == currentTodo?.id}" [ngClass]="{'current-todo':todo?.id == currentTodo?.id}"
matRipple matRipple
[@animate]="{value:'*',params:{y:'100%'}}"> [@animate]="{value:'*',params:{y:'100%'}}">
</fuse-todo-list-item> </todo-list-item>
</div> </div>

View File

@ -1,94 +1,125 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subject } from 'rxjs';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { Todo } from '../todo.model'; import { Todo } from 'app/main/apps/todo/todo.model';
import { TodoService } from '../todo.service'; import { TodoService } from 'app/main/apps/todo/todo.service';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector : 'fuse-todo-list', selector : 'todo-list',
templateUrl: './todo-list.component.html', templateUrl: './todo-list.component.html',
styleUrls : ['./todo-list.component.scss'], styleUrls : ['./todo-list.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseTodoListComponent implements OnInit, OnDestroy export class TodoListComponent implements OnInit, OnDestroy
{ {
todos: Todo[]; todos: Todo[];
currentTodo: Todo; currentTodo: Todo;
onTodosChanged: Subscription; // Private
onCurrentTodoChanged: Subscription; private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {ActivatedRoute} _activatedRoute
* @param {TodoService} _todoService
* @param {Location} _location
*/
constructor( constructor(
private route: ActivatedRoute, private _activatedRoute: ActivatedRoute,
private todoService: TodoService, private _todoService: TodoService,
private location: Location private _location: Location
) )
{ {
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
// Subscribe to update todos on changes // Subscribe to update todos on changes
this.onTodosChanged = this._todoService.onTodosChanged
this.todoService.onTodosChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(todos => { .subscribe(todos => {
this.todos = todos; this.todos = todos;
}); });
// Subscribe to update current todo on changes // Subscribe to update current todo on changes
this.onCurrentTodoChanged = this._todoService.onCurrentTodoChanged
this.todoService.onCurrentTodoChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(currentTodo => { .subscribe(currentTodo => {
if ( !currentTodo ) if ( !currentTodo )
{
// Set the current todo id to null to deselect the current todo
this.currentTodo = null;
// Handle the location changes
const tagHandle = this._activatedRoute.snapshot.params.tagHandle,
filterHandle = this._activatedRoute.snapshot.params.filterHandle;
if ( tagHandle )
{ {
// Set the current todo id to null to deselect the current todo this._location.go('apps/todo/tag/' + tagHandle);
this.currentTodo = null; }
else if ( filterHandle )
// Handle the location changes {
const tagHandle = this.route.snapshot.params.tagHandle, this._location.go('apps/todo/filter/' + filterHandle);
filterHandle = this.route.snapshot.params.filterHandle;
if ( tagHandle )
{
this.location.go('apps/todo/tag/' + tagHandle);
}
else if ( filterHandle )
{
this.location.go('apps/todo/filter/' + filterHandle);
}
else
{
this.location.go('apps/todo/all');
}
} }
else else
{ {
this.currentTodo = currentTodo; this._location.go('apps/todo/all');
} }
}); }
} else
{
ngOnDestroy() this.currentTodo = currentTodo;
{ }
this.onTodosChanged.unsubscribe(); });
this.onCurrentTodoChanged.unsubscribe();
} }
/** /**
* Read todo * On destroy
* @param todoId
*/ */
readTodo(todoId) ngOnDestroy(): void
{ {
// Set current todo // Unsubscribe from all subscriptions
this.todoService.setCurrentTodo(todoId); this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
onDrop(ev) // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Read todo
*
* @param todoId
*/
readTodo(todoId): void
{
// Set current todo
this._todoService.setCurrentTodo(todoId);
}
/**
* On drop
*
* @param ev
*/
onDrop(ev): void
{ {
} }

View File

@ -9,7 +9,7 @@
<!-- SIDENAV --> <!-- SIDENAV -->
<mat-sidenav class="sidenav" position="start" opened="true" mode="side" <mat-sidenav class="sidenav" position="start" opened="true" mode="side"
fuseMatSidenavHelper="carded-left-sidenav" mat-is-locked-open="gt-md"> fuseMatSidenavHelper="carded-left-sidenav" mat-is-locked-open="gt-md">
<fuse-todo-main-sidenav></fuse-todo-main-sidenav> <todo-main-sidenav></todo-main-sidenav>
</mat-sidenav> </mat-sidenav>
<!-- / SIDENAV --> <!-- / SIDENAV -->
@ -73,7 +73,7 @@
</div> </div>
<div *ngIf="currentTodo" fxHide.gt-lg> <div *ngIf="currentTodo" fxHide.gt-lg>
<button mat-icon-button (click)="deSelectCurrentTodo()"> <button mat-icon-button (click)="deselectCurrentTodo()">
<mat-icon>arrow_back</mat-icon> <mat-icon>arrow_back</mat-icon>
</button> </button>
</div> </div>
@ -84,9 +84,9 @@
<!-- CONTENT --> <!-- CONTENT -->
<div class="content" fxFlexAlign="row"> <div class="content" fxFlexAlign="row">
<fuse-todo-list fusePerfectScrollbar fxFlex></fuse-todo-list> <todo-list fusePerfectScrollbar fxFlex></todo-list>
<fuse-todo-details fusePerfectScrollbar fxFlex></fuse-todo-details> <todo-details fusePerfectScrollbar fxFlex></todo-details>
</div> </div>
<!-- / CONTENT --> <!-- / CONTENT -->

View File

@ -39,16 +39,16 @@
@include media-breakpoint-down(lg) { @include media-breakpoint-down(lg) {
fuse-todo-list { todo-list {
border-right: 0; border-right: 0;
} }
fuse-todo-list, todo-list,
fuse-todo-details { todo-details {
flex: 1 0 100%; flex: 1 0 100%;
} }
fuse-todo-details { todo-details {
display: none !important; display: none !important;
} }
@ -64,11 +64,11 @@
.content { .content {
fuse-todo-list { todo-list {
display: none !important; display: none !important;
} }
fuse-todo-details { todo-details {
display: flex !important; display: flex !important;
} }
} }

View File

@ -1,21 +1,20 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { Subject } from 'rxjs';
import { Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
import { Todo } from './todo.model'; import { Todo } from 'app/main/apps/todo/todo.model';
import { TodoService } from './todo.service'; import { TodoService } from 'app/main/apps/todo/todo.service';
@Component({ @Component({
selector : 'fuse-todo', selector : 'todo',
templateUrl: './todo.component.html', templateUrl: './todo.component.html',
styleUrls : ['./todo.component.scss'], styleUrls : ['./todo.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseTodoComponent implements OnInit, OnDestroy export class TodoComponent implements OnInit, OnDestroy
{ {
hasSelectedTodos: boolean; hasSelectedTodos: boolean;
isIndeterminate: boolean; isIndeterminate: boolean;
@ -24,91 +23,136 @@ export class FuseTodoComponent implements OnInit, OnDestroy
searchInput: FormControl; searchInput: FormControl;
currentTodo: Todo; currentTodo: Todo;
onSelectedTodosChanged: Subscription; // Private
onFiltersChanged: Subscription; private _unsubscribeAll: Subject<any>;
onTagsChanged: Subscription;
onCurrentTodoChanged: Subscription;
constructor(private todoService: TodoService) /**
* Constructor
*
* @param {TodoService} _todoService
*/
constructor(
private _todoService: TodoService
)
{ {
// Set the defaults
this.searchInput = new FormControl(''); this.searchInput = new FormControl('');
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onSelectedTodosChanged = this._todoService.onSelectedTodosChanged
this.todoService.onSelectedTodosChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(selectedTodos => { .subscribe(selectedTodos => {
setTimeout(() => { setTimeout(() => {
this.hasSelectedTodos = selectedTodos.length > 0; this.hasSelectedTodos = selectedTodos.length > 0;
this.isIndeterminate = (selectedTodos.length !== this.todoService.todos.length && selectedTodos.length > 0); this.isIndeterminate = (selectedTodos.length !== this._todoService.todos.length && selectedTodos.length > 0);
}, 0); }, 0);
}); });
this.onFiltersChanged = this._todoService.onFiltersChanged
this.todoService.onFiltersChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(folders => { .subscribe(folders => {
this.filters = this.todoService.filters; this.filters = this._todoService.filters;
}); });
this.onTagsChanged = this._todoService.onTagsChanged
this.todoService.onTagsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(tags => { .subscribe(tags => {
this.tags = this.todoService.tags; this.tags = this._todoService.tags;
}); });
this.searchInput.valueChanges.pipe( this.searchInput.valueChanges
debounceTime(300), .pipe(
distinctUntilChanged() takeUntil(this._unsubscribeAll),
).subscribe(searchText => { debounceTime(300),
this.todoService.onSearchTextChanged.next(searchText); distinctUntilChanged()
}); )
.subscribe(searchText => {
this._todoService.onSearchTextChanged.next(searchText);
});
this.onCurrentTodoChanged = this._todoService.onCurrentTodoChanged
this.todoService.onCurrentTodoChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(([currentTodo, formType]) => { .subscribe(([currentTodo, formType]) => {
if ( !currentTodo ) if ( !currentTodo )
{ {
this.currentTodo = null; this.currentTodo = null;
} }
else else
{ {
this.currentTodo = currentTodo; this.currentTodo = currentTodo;
} }
}); });
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onSelectedTodosChanged.unsubscribe(); // Unsubscribe from all subscriptions
this.onFiltersChanged.unsubscribe(); this._unsubscribeAll.next();
this.onTagsChanged.unsubscribe(); this._unsubscribeAll.complete();
this.onCurrentTodoChanged.unsubscribe();
} }
deSelectCurrentTodo() // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Deselect current todo
*/
deselectCurrentTodo(): void
{ {
this.todoService.onCurrentTodoChanged.next([null, null]); this._todoService.onCurrentTodoChanged.next([null, null]);
} }
toggleSelectAll() /**
* Toggle select all
*/
toggleSelectAll(): void
{ {
this.todoService.toggleSelectAll(); this._todoService.toggleSelectAll();
} }
selectTodos(filterParameter?, filterValue?) /**
* Select todos
*
* @param filterParameter
* @param filterValue
*/
selectTodos(filterParameter?, filterValue?): void
{ {
this.todoService.selectTodos(filterParameter, filterValue); this._todoService.selectTodos(filterParameter, filterValue);
} }
deselectTodos() /**
* Deselect todos
*/
deselectTodos(): void
{ {
this.todoService.deselectTodos(); this._todoService.deselectTodos();
} }
toggleTagOnSelectedTodos(tagId) /**
* Toggle tag on selected todos
*
* @param tagId
*/
toggleTagOnSelectedTodos(tagId): void
{ {
this.todoService.toggleTagOnSelectedTodos(tagId); this._todoService.toggleTagOnSelectedTodos(tagId);
} }
} }

View File

@ -18,6 +18,11 @@ export class Todo
} }
]; ];
/**
* Constructor
*
* @param todo
*/
constructor(todo) constructor(todo)
{ {
{ {
@ -34,22 +39,34 @@ export class Todo
} }
} }
toggleStar() /**
* Toggle star
*/
toggleStar(): void
{ {
this.starred = !this.starred; this.starred = !this.starred;
} }
toggleImportant() /**
* Toggle important
*/
toggleImportant(): void
{ {
this.important = !this.important; this.important = !this.important;
} }
toggleCompleted() /**
* Toggle completed
*/
toggleCompleted(): void
{ {
this.completed = !this.completed; this.completed = !this.completed;
} }
toggleDeleted() /**
* Toggle deleted
*/
toggleDeleted(): void
{ {
this.deleted = !this.deleted; this.deleted = !this.deleted;
} }

View File

@ -1,58 +1,56 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { MatButtonModule, MatCheckboxModule, MatDatepickerModule, MatFormFieldModule, MatIconModule, MatInputModule, MatMenuModule, MatRippleModule, MatSelectModule, MatSidenavModule } from '@angular/material'; import { MatButtonModule, MatCheckboxModule, MatDatepickerModule, MatFormFieldModule, MatIconModule, MatInputModule, MatMenuModule, MatRippleModule, MatSelectModule, MatSidenavModule } from '@angular/material';
import { NgxDnDModule } from '@swimlane/ngx-dnd'; import { NgxDnDModule } from '@swimlane/ngx-dnd';
import { FuseSharedModule } from '@fuse/shared.module'; import { FuseSharedModule } from '@fuse/shared.module';
import { TodoService } from './todo.service'; import { TodoService } from 'app/main/apps/todo/todo.service';
import { FuseTodoComponent } from './todo.component'; import { TodoComponent } from 'app/main/apps/todo/todo.component';
import { FuseTodoMainSidenavComponent } from './sidenavs/main/main-sidenav.component'; import { TodoMainSidenavComponent } from 'app/main/apps/todo/sidenavs/main/main-sidenav.component';
import { FuseTodoListItemComponent } from './todo-list/todo-list-item/todo-list-item.component'; import { TodoListItemComponent } from 'app/main/apps/todo/todo-list/todo-list-item/todo-list-item.component';
import { FuseTodoListComponent } from './todo-list/todo-list.component'; import { TodoListComponent } from 'app/main/apps/todo/todo-list/todo-list.component';
import { FuseTodoDetailsComponent } from './todo-details/todo-details.component'; import { TodoDetailsComponent } from 'app/main/apps/todo/todo-details/todo-details.component';
const routes: Routes = [ const routes: Routes = [
{ {
path : 'all', path : 'all',
component: FuseTodoComponent, component: TodoComponent,
resolve : { resolve : {
todo: TodoService todo: TodoService
} }
}, },
{ {
path : 'all/:todoId', path : 'all/:todoId',
component: FuseTodoComponent, component: TodoComponent,
resolve : { resolve : {
todo: TodoService todo: TodoService
} }
}, },
{ {
path : 'tag/:tagHandle', path : 'tag/:tagHandle',
component: FuseTodoComponent, component: TodoComponent,
resolve : { resolve : {
todo: TodoService todo: TodoService
} }
}, },
{ {
path : 'tag/:tagHandle/:todoId', path : 'tag/:tagHandle/:todoId',
component: FuseTodoComponent, component: TodoComponent,
resolve : { resolve : {
todo: TodoService todo: TodoService
} }
}, },
{ {
path : 'filter/:filterHandle', path : 'filter/:filterHandle',
component: FuseTodoComponent, component: TodoComponent,
resolve : { resolve : {
todo: TodoService todo: TodoService
} }
}, },
{ {
path : 'filter/:filterHandle/:todoId', path : 'filter/:filterHandle/:todoId',
component: FuseTodoComponent, component: TodoComponent,
resolve : { resolve : {
todo: TodoService todo: TodoService
} }
@ -65,11 +63,11 @@ const routes: Routes = [
@NgModule({ @NgModule({
declarations: [ declarations: [
FuseTodoComponent, TodoComponent,
FuseTodoMainSidenavComponent, TodoMainSidenavComponent,
FuseTodoListItemComponent, TodoListItemComponent,
FuseTodoListComponent, TodoListComponent,
FuseTodoDetailsComponent TodoDetailsComponent
], ],
imports : [ imports : [
RouterModule.forChild(routes), RouterModule.forChild(routes),
@ -87,7 +85,7 @@ const routes: Routes = [
NgxDnDModule, NgxDnDModule,
FuseSharedModule, FuseSharedModule
], ],
providers : [ providers : [
TodoService TodoService

View File

@ -6,7 +6,7 @@ import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { FuseUtils } from '@fuse/utils'; import { FuseUtils } from '@fuse/utils';
import { Todo } from './todo.model'; import { Todo } from 'app/main/apps/todo/todo.model';
@Injectable() @Injectable()
export class TodoService implements Resolve<any> export class TodoService implements Resolve<any>
@ -14,31 +14,45 @@ export class TodoService implements Resolve<any>
todos: Todo[]; todos: Todo[];
selectedTodos: Todo[]; selectedTodos: Todo[];
currentTodo: Todo; currentTodo: Todo;
searchText = ''; searchText: string;
filters: any[]; filters: any[];
tags: any[]; tags: any[];
routeParams: any; routeParams: any;
onTodosChanged: BehaviorSubject<any> = new BehaviorSubject([]); onTodosChanged: BehaviorSubject<any>;
onSelectedTodosChanged: BehaviorSubject<any> = new BehaviorSubject([]); onSelectedTodosChanged: BehaviorSubject<any>;
onCurrentTodoChanged: BehaviorSubject<any> = new BehaviorSubject([]); onCurrentTodoChanged: BehaviorSubject<any>;
onFiltersChanged: BehaviorSubject<any>;
onFiltersChanged: BehaviorSubject<any> = new BehaviorSubject([]); onTagsChanged: BehaviorSubject<any>;
onTagsChanged: BehaviorSubject<any> = new BehaviorSubject([]); onSearchTextChanged: BehaviorSubject<any>;
onSearchTextChanged: BehaviorSubject<any> = new BehaviorSubject(''); onNewTodoClicked: Subject<any>;
onNewTodoClicked: Subject<any> = new Subject();
/**
* Constructor
*
* @param {HttpClient} _httpClient
* @param {Location} _location
*/
constructor( constructor(
private http: HttpClient, private _httpClient: HttpClient,
private location: Location // Set current todo private _location: Location
) )
{ {
// Set the defaults
this.selectedTodos = []; this.selectedTodos = [];
this.searchText = '';
this.onTodosChanged = new BehaviorSubject([]);
this.onSelectedTodosChanged = new BehaviorSubject([]);
this.onCurrentTodoChanged = new BehaviorSubject([]);
this.onFiltersChanged = new BehaviorSubject([]);
this.onTagsChanged = new BehaviorSubject([]);
this.onSearchTextChanged = new BehaviorSubject('');
this.onNewTodoClicked = new Subject();
} }
/** /**
* Resolve * Resolver
*
* @param {ActivatedRouteSnapshot} route * @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state * @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any} * @returns {Observable<any> | Promise<any> | any}
@ -85,12 +99,13 @@ export class TodoService implements Resolve<any>
/** /**
* Get all filters * Get all filters
*
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
getFilters(): Promise<any> getFilters(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/todo-filters') this._httpClient.get('api/todo-filters')
.subscribe((response: any) => { .subscribe((response: any) => {
this.filters = response; this.filters = response;
this.onFiltersChanged.next(this.filters); this.onFiltersChanged.next(this.filters);
@ -101,12 +116,13 @@ export class TodoService implements Resolve<any>
/** /**
* Get all tags * Get all tags
*
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
getTags(): Promise<any> getTags(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/todo-tags') this._httpClient.get('api/todo-tags')
.subscribe((response: any) => { .subscribe((response: any) => {
this.tags = response; this.tags = response;
this.onTagsChanged.next(this.tags); this.onTagsChanged.next(this.tags);
@ -117,6 +133,7 @@ export class TodoService implements Resolve<any>
/** /**
* Get todos * Get todos
*
* @returns {Promise<Todo[]>} * @returns {Promise<Todo[]>}
*/ */
getTodos(): Promise<Todo[]> getTodos(): Promise<Todo[]>
@ -136,6 +153,7 @@ export class TodoService implements Resolve<any>
/** /**
* Get todos by params * Get todos by params
*
* @param handle * @param handle
* @returns {Promise<Todo[]>} * @returns {Promise<Todo[]>}
*/ */
@ -143,7 +161,7 @@ export class TodoService implements Resolve<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/todo-todos') this._httpClient.get('api/todo-todos')
.subscribe((todos: any) => { .subscribe((todos: any) => {
this.todos = todos.map(todo => { this.todos = todos.map(todo => {
return new Todo(todo); return new Todo(todo);
@ -160,6 +178,7 @@ export class TodoService implements Resolve<any>
/** /**
* Get todos by filter * Get todos by filter
*
* @param handle * @param handle
* @returns {Promise<Todo[]>} * @returns {Promise<Todo[]>}
*/ */
@ -175,7 +194,7 @@ export class TodoService implements Resolve<any>
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/todo-todos?' + param) this._httpClient.get('api/todo-todos?' + param)
.subscribe((todos: any) => { .subscribe((todos: any) => {
this.todos = todos.map(todo => { this.todos = todos.map(todo => {
@ -194,18 +213,19 @@ export class TodoService implements Resolve<any>
/** /**
* Get todos by tag * Get todos by tag
*
* @param handle * @param handle
* @returns {Promise<Todo[]>} * @returns {Promise<Todo[]>}
*/ */
getTodosByTag(handle): Promise<Todo[]> getTodosByTag(handle): Promise<Todo[]>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/todo-tags?handle=' + handle) this._httpClient.get('api/todo-tags?handle=' + handle)
.subscribe((tags: any) => { .subscribe((tags: any) => {
const tagId = tags[0].id; const tagId = tags[0].id;
this.http.get('api/todo-todos?tags=' + tagId) this._httpClient.get('api/todo-todos?tags=' + tagId)
.subscribe((todos: any) => { .subscribe((todos: any) => {
this.todos = todos.map(todo => { this.todos = todos.map(todo => {
@ -225,9 +245,10 @@ export class TodoService implements Resolve<any>
/** /**
* Toggle selected todo by id * Toggle selected todo by id
*
* @param id * @param id
*/ */
toggleSelectedTodo(id) toggleSelectedTodo(id): void
{ {
// First, check if we already have that todo as selected... // First, check if we already have that todo as selected...
if ( this.selectedTodos.length > 0 ) if ( this.selectedTodos.length > 0 )
@ -267,7 +288,7 @@ export class TodoService implements Resolve<any>
/** /**
* Toggle select all * Toggle select all
*/ */
toggleSelectAll() toggleSelectAll(): void
{ {
if ( this.selectedTodos.length > 0 ) if ( this.selectedTodos.length > 0 )
{ {
@ -280,7 +301,13 @@ export class TodoService implements Resolve<any>
} }
selectTodos(filterParameter?, filterValue?) /**
* Select todos
*
* @param filterParameter
* @param filterValue
*/
selectTodos(filterParameter?, filterValue?): void
{ {
this.selectedTodos = []; this.selectedTodos = [];
@ -302,7 +329,10 @@ export class TodoService implements Resolve<any>
this.onSelectedTodosChanged.next(this.selectedTodos); this.onSelectedTodosChanged.next(this.selectedTodos);
} }
deselectTodos() /**
* Deselect todos
*/
deselectTodos(): void
{ {
this.selectedTodos = []; this.selectedTodos = [];
@ -312,9 +342,10 @@ export class TodoService implements Resolve<any>
/** /**
* Set current todo by id * Set current todo by id
*
* @param id * @param id
*/ */
setCurrentTodo(id) setCurrentTodo(id): void
{ {
this.currentTodo = this.todos.find(todo => { this.currentTodo = this.todos.find(todo => {
return todo.id === id; return todo.id === id;
@ -327,30 +358,37 @@ export class TodoService implements Resolve<any>
if ( tagHandle ) if ( tagHandle )
{ {
this.location.go('apps/todo/tag/' + tagHandle + '/' + id); this._location.go('apps/todo/tag/' + tagHandle + '/' + id);
} }
else if ( filterHandle ) else if ( filterHandle )
{ {
this.location.go('apps/todo/filter/' + filterHandle + '/' + id); this._location.go('apps/todo/filter/' + filterHandle + '/' + id);
} }
else else
{ {
this.location.go('apps/todo/all/' + id); this._location.go('apps/todo/all/' + id);
} }
} }
/** /**
* Toggle tag on selected todos * Toggle tag on selected todos
*
* @param tagId * @param tagId
*/ */
toggleTagOnSelectedTodos(tagId) toggleTagOnSelectedTodos(tagId): void
{ {
this.selectedTodos.map(todo => { this.selectedTodos.map(todo => {
this.toggleTagOnTodo(tagId, todo); this.toggleTagOnTodo(tagId, todo);
}); });
} }
toggleTagOnTodo(tagId, todo) /**
* Toggle tag on todo
*
* @param tagId
* @param todo
*/
toggleTagOnTodo(tagId, todo): void
{ {
const index = todo.tags.indexOf(tagId); const index = todo.tags.indexOf(tagId);
@ -366,7 +404,14 @@ export class TodoService implements Resolve<any>
this.updateTodo(todo); this.updateTodo(todo);
} }
hasTag(tagId, todo) /**
* Has tag?
*
* @param tagId
* @param todo
* @returns {boolean}
*/
hasTag(tagId, todo): any
{ {
if ( !todo.tags ) if ( !todo.tags )
{ {
@ -378,14 +423,15 @@ export class TodoService implements Resolve<any>
/** /**
* Update the todo * Update the todo
*
* @param todo * @param todo
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
updateTodo(todo) updateTodo(todo): any
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.post('api/todo-todos/' + todo.id, {...todo}) this._httpClient.post('api/todo-todos/' + todo.id, {...todo})
.subscribe(response => { .subscribe(response => {
this.getTodos().then(todos => { this.getTodos().then(todos => {

View File

@ -91,8 +91,7 @@ const routes = [
FuseCountdownModule, FuseCountdownModule,
FuseHighlightModule, FuseHighlightModule,
FuseMaterialColorPickerModule, FuseMaterialColorPickerModule,
FuseWidgetModule, FuseWidgetModule
FuseAngularMaterialModule
] ]
}) })
export class ComponentsModule export class ComponentsModule

View File

@ -90,7 +90,7 @@
templateUrl: './mail.component.html', templateUrl: './mail.component.html',
styleUrls : ['./mail.component.scss'] styleUrls : ['./mail.component.scss']
}) })
export class FuseMailComponent export class MailComponent
{ {
constructor(private translationLoader: FuseTranslationLoaderService) constructor(private translationLoader: FuseTranslationLoaderService)
{ {

View File

@ -1,26 +1,38 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseConfigService } from '@fuse/services/config.service'; import { FuseConfigService } from '@fuse/services/config.service';
import { fuseAnimations } from '@fuse/animations'; import { fuseAnimations } from '@fuse/animations';
@Component({ @Component({
selector : 'fuse-coming-soon', selector : 'coming-soon',
templateUrl: './coming-soon.component.html', templateUrl: './coming-soon.component.html',
styleUrls : ['./coming-soon.component.scss'], styleUrls : ['./coming-soon.component.scss'],
animations : fuseAnimations animations : fuseAnimations
}) })
export class FuseComingSoonComponent implements OnInit export class ComingSoonComponent implements OnInit, OnDestroy
{ {
comingSoonForm: FormGroup; comingSoonForm: FormGroup;
comingSoonFormErrors: any; comingSoonFormErrors: any;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {FuseConfigService} _fuseConfigService
* @param {FormBuilder} _formBuilder
*/
constructor( constructor(
private fuseConfig: FuseConfigService, private _fuseConfigService: FuseConfigService,
private formBuilder: FormBuilder private _formBuilder: FormBuilder
) )
{ {
this.fuseConfig.config = { // Configure the layout
this._fuseConfigService.config = {
layout: { layout: {
navigation: 'none', navigation: 'none',
toolbar : 'none', toolbar : 'none',
@ -28,23 +40,53 @@ export class FuseComingSoonComponent implements OnInit
} }
}; };
// Set the defaults
this.comingSoonFormErrors = { this.comingSoonFormErrors = {
email: {} email: {}
}; };
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.comingSoonForm = this.formBuilder.group({ this.comingSoonForm = this._formBuilder.group({
email: ['', [Validators.required, Validators.email]] email: ['', [Validators.required, Validators.email]]
}); });
this.comingSoonForm.valueChanges.subscribe(() => { this.comingSoonForm.valueChanges
this.onRegisterFormValuesChanged(); .pipe(takeUntil(this._unsubscribeAll))
}); .subscribe(() => {
this.onRegisterFormValuesChanged();
});
} }
onRegisterFormValuesChanged() /**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* On form values changed
*/
onRegisterFormValuesChanged(): void
{ {
for ( const field in this.comingSoonFormErrors ) for ( const field in this.comingSoonFormErrors )
{ {

View File

@ -6,18 +6,18 @@ import { MatButtonModule, MatFormFieldModule, MatInputModule } from '@angular/ma
import { FuseSharedModule } from '@fuse/shared.module'; import { FuseSharedModule } from '@fuse/shared.module';
import { FuseCountdownModule } from '@fuse/components'; import { FuseCountdownModule } from '@fuse/components';
import { FuseComingSoonComponent } from './coming-soon.component'; import { ComingSoonComponent } from 'app/main/pages/coming-soon/coming-soon.component';
const routes = [ const routes = [
{ {
path : 'coming-soon', path : 'coming-soon',
component: FuseComingSoonComponent component: ComingSoonComponent
} }
]; ];
@NgModule({ @NgModule({
declarations: [ declarations: [
FuseComingSoonComponent ComingSoonComponent
], ],
imports : [ imports : [
RouterModule.forChild(routes), RouterModule.forChild(routes),

View File

@ -7,13 +7,19 @@ import { FuseConfigService } from '@fuse/services/config.service';
templateUrl: './error-404.component.html', templateUrl: './error-404.component.html',
styleUrls : ['./error-404.component.scss'] styleUrls : ['./error-404.component.scss']
}) })
export class FuseError404Component export class Error404Component
{ {
/**
* Constructor
*
* @param {FuseConfigService} _fuseConfigService
*/
constructor( constructor(
private fuseConfig: FuseConfigService private _fuseConfigService: FuseConfigService
) )
{ {
this.fuseConfig.config = { // Configure the layout
this._fuseConfigService.config = {
layout: { layout: {
navigation: 'none', navigation: 'none',
toolbar : 'none', toolbar : 'none',

View File

@ -1,22 +1,21 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { MatIconModule } from '@angular/material'; import { MatIconModule } from '@angular/material';
import { FuseSharedModule } from '@fuse/shared.module'; import { FuseSharedModule } from '@fuse/shared.module';
import { FuseError404Component } from './error-404.component'; import { Error404Component } from 'app/main/pages/errors/404/error-404.component';
const routes = [ const routes = [
{ {
path : 'errors/error-404', path : 'errors/error-404',
component: FuseError404Component component: Error404Component
} }
]; ];
@NgModule({ @NgModule({
declarations: [ declarations: [
FuseError404Component Error404Component
], ],
imports : [ imports : [
RouterModule.forChild(routes), RouterModule.forChild(routes),

View File

@ -3,16 +3,17 @@ import { Component } from '@angular/core';
import { FuseConfigService } from '@fuse/services/config.service'; import { FuseConfigService } from '@fuse/services/config.service';
@Component({ @Component({
selector : 'fuse-error-500', selector : 'error-500',
templateUrl: './error-500.component.html', templateUrl: './error-500.component.html',
styleUrls : ['./error-500.component.scss'] styleUrls : ['./error-500.component.scss']
}) })
export class FuseError500Component export class Error500Component
{ {
constructor( constructor(
private fuseConfig: FuseConfigService private fuseConfig: FuseConfigService
) )
{ {
// Configure the layout
this.fuseConfig.config = { this.fuseConfig.config = {
layout: { layout: {
navigation: 'none', navigation: 'none',

View File

@ -3,18 +3,18 @@ import { RouterModule } from '@angular/router';
import { FuseSharedModule } from '@fuse/shared.module'; import { FuseSharedModule } from '@fuse/shared.module';
import { FuseError500Component } from './error-500.component'; import { Error500Component } from 'app/main/pages/errors/500/error-500.component';
const routes = [ const routes = [
{ {
path : 'errors/error-500', path : 'errors/error-500',
component: FuseError500Component component: Error500Component
} }
]; ];
@NgModule({ @NgModule({
declarations: [ declarations: [
FuseError500Component Error500Component
], ],
imports : [ imports : [
RouterModule.forChild(routes), RouterModule.forChild(routes),

View File

@ -1,64 +1,107 @@
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { Subject } from 'rxjs';
import { Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { FuseUtils } from '@fuse/utils'; import { FuseUtils } from '@fuse/utils';
import { FaqService } from './faq.service'; import { FaqService } from 'app/main/pages/faq/faq.service';
@Component({ @Component({
selector : 'fuse-faq', selector : 'faq',
templateUrl: './faq.component.html', templateUrl: './faq.component.html',
styleUrls : ['./faq.component.scss'] styleUrls : ['./faq.component.scss']
}) })
export class FuseFaqComponent implements OnInit, OnDestroy export class FaqComponent implements OnInit, OnDestroy
{ {
faqs: any; faqs: any;
faqsFiltered: any; faqsFiltered: any;
step = 0; step: number;
searchInput; searchInput: any;
onFaqsChanged: Subscription;
constructor(private faqService: FaqService) // Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {FaqService} _faqService
*/
constructor(
private _faqService: FaqService
)
{ {
// Set the defaults
this.searchInput = new FormControl(''); this.searchInput = new FormControl('');
this.step = 0;
// Set the private defaults
this._unsubscribeAll = new Subject();
} }
ngOnInit() // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{ {
this.onFaqsChanged = this._faqService.onFaqsChanged
this.faqService.onFaqsChanged .pipe(takeUntil(this._unsubscribeAll))
.subscribe(response => { .subscribe(response => {
this.faqs = response; this.faqs = response;
this.faqsFiltered = response; this.faqsFiltered = response;
}); });
this.searchInput.valueChanges.pipe( this.searchInput.valueChanges
debounceTime(300), .pipe(
distinctUntilChanged() takeUntil(this._unsubscribeAll),
).subscribe(searchText => { debounceTime(300),
this.faqsFiltered = FuseUtils.filterArrayByString(this.faqs, searchText); distinctUntilChanged()
}); )
.subscribe(searchText => {
this.faqsFiltered = FuseUtils.filterArrayByString(this.faqs, searchText);
});
} }
ngOnDestroy() /**
* On destroy
*/
ngOnDestroy(): void
{ {
this.onFaqsChanged.unsubscribe(); // Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
} }
setStep(index: number) // -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Set step
*
* @param {number} index
*/
setStep(index: number): void
{ {
this.step = index; this.step = index;
} }
nextStep() /**
* Next step
*/
nextStep(): void
{ {
this.step++; this.step++;
} }
prevStep() /**
* Previous step
*/
prevStep(): void
{ {
this.step--; this.step--;
} }

View File

@ -5,13 +5,13 @@ import { MatExpansionModule, MatIconModule } from '@angular/material';
import { FuseSharedModule } from '@fuse/shared.module'; import { FuseSharedModule } from '@fuse/shared.module';
import { FaqService } from './faq.service'; import { FaqService } from 'app/main/pages/faq/faq.service';
import { FuseFaqComponent } from './faq.component'; import { FaqComponent } from 'app/main/pages/faq/faq.component';
const routes = [ const routes = [
{ {
path : 'faq', path : 'faq',
component: FuseFaqComponent, component: FaqComponent,
resolve : { resolve : {
faq: FaqService faq: FaqService
} }
@ -20,7 +20,7 @@ const routes = [
@NgModule({ @NgModule({
declarations: [ declarations: [
FuseFaqComponent FaqComponent
], ],
imports : [ imports : [
RouterModule.forChild(routes), RouterModule.forChild(routes),

View File

@ -7,15 +7,24 @@ import { BehaviorSubject, Observable } from 'rxjs';
export class FaqService implements Resolve<any> export class FaqService implements Resolve<any>
{ {
faqs: any; faqs: any;
onFaqsChanged: BehaviorSubject<any>;
onFaqsChanged: BehaviorSubject<any> = new BehaviorSubject({}); /**
* Constructor
constructor(private http: HttpClient) *
* @param {HttpClient} _httpClient
*/
constructor(
private _httpClient: HttpClient
)
{ {
// Set the defaults
this.onFaqsChanged = new BehaviorSubject({});
} }
/** /**
* Resolve * Resolver
*
* @param {ActivatedRouteSnapshot} route * @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state * @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any} * @returns {Observable<any> | Promise<any> | any}
@ -41,7 +50,7 @@ export class FaqService implements Resolve<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/faq') this._httpClient.get('api/faq')
.subscribe((response: any) => { .subscribe((response: any) => {
this.faqs = response; this.faqs = response;
this.onFaqsChanged.next(this.faqs); this.onFaqsChanged.next(this.faqs);

View File

@ -1,22 +1,52 @@
import { Component } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { InvoiceService } from '../invoice.service'; import { InvoiceService } from 'app/main/pages/invoices/invoice.service';
@Component({ @Component({
selector : 'fuse-invoice-compact', selector : 'invoice-compact',
templateUrl: './compact.component.html', templateUrl: './compact.component.html',
styleUrls : ['./compact.component.scss'] styleUrls : ['./compact.component.scss']
}) })
export class FuseInvoiceCompactComponent export class InvoiceCompactComponent implements OnInit, OnDestroy
{ {
invoice: any; invoice: any;
constructor(private invoiceService: InvoiceService) // Private
private _unsubscribeAll: Subject<any>;
constructor(
private _invoiceService: InvoiceService
)
{ {
this.invoiceService.invoiceOnChanged // Set the private defaults
this._unsubscribeAll = new Subject();
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
this._invoiceService.invoiceOnChanged
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((invoice) => { .subscribe((invoice) => {
this.invoice = invoice; this.invoice = invoice;
}); });
} }
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
} }

View File

@ -3,13 +3,13 @@ import { RouterModule } from '@angular/router';
import { FuseSharedModule } from '@fuse/shared.module'; import { FuseSharedModule } from '@fuse/shared.module';
import { InvoiceService } from '../invoice.service'; import { InvoiceService } from 'app/main/pages/invoices/invoice.service';
import { FuseInvoiceCompactComponent } from './compact.component'; import { InvoiceCompactComponent } from 'app/main/pages/invoices/compact/compact.component';
const routes = [ const routes = [
{ {
path : 'invoices/compact', path : 'invoices/compact',
component: FuseInvoiceCompactComponent, component: InvoiceCompactComponent,
resolve : { resolve : {
search: InvoiceService search: InvoiceService
} }
@ -18,7 +18,7 @@ const routes = [
@NgModule({ @NgModule({
declarations: [ declarations: [
FuseInvoiceCompactComponent InvoiceCompactComponent
], ],
imports : [ imports : [
RouterModule.forChild(routes), RouterModule.forChild(routes),

View File

@ -3,19 +3,28 @@ import { HttpClient } from '@angular/common/http';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
@Injectable() @Injectable()
export class InvoiceService implements Resolve<any> export class InvoiceService implements Resolve<any>
{ {
invoice: any; invoice: any;
invoiceOnChanged: BehaviorSubject<any> = new BehaviorSubject({}); invoiceOnChanged: BehaviorSubject<any>;
constructor(private http: HttpClient) /**
* Constructor
*
* @param {HttpClient} _httpClient
*/
constructor(
private _httpClient: HttpClient
)
{ {
// Set the defaults
this.invoiceOnChanged = new BehaviorSubject({});
} }
/** /**
* Resolve * Resolver
*
* @param {ActivatedRouteSnapshot} route * @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state * @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any} * @returns {Observable<any> | Promise<any> | any}
@ -41,7 +50,7 @@ export class InvoiceService implements Resolve<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/invoice') this._httpClient.get('api/invoice')
.subscribe((timeline: any) => { .subscribe((timeline: any) => {
this.invoice = timeline; this.invoice = timeline;
this.invoiceOnChanged.next(this.invoice); this.invoiceOnChanged.next(this.invoice);

View File

@ -1,22 +1,58 @@
import { Component } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { InvoiceService } from '../invoice.service'; import { InvoiceService } from 'app/main/pages/invoices/invoice.service';
@Component({ @Component({
selector : 'fuse-invoice-modern', selector : 'invoice-modern',
templateUrl: './modern.component.html', templateUrl: './modern.component.html',
styleUrls : ['./modern.component.scss'] styleUrls : ['./modern.component.scss']
}) })
export class FuseInvoiceModernComponent export class InvoiceModernComponent implements OnInit, OnDestroy
{ {
invoice: any; invoice: any;
constructor(private invoiceService: InvoiceService) // Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {InvoiceService} _invoiceService
*/
constructor(
private _invoiceService: InvoiceService
)
{ {
this.invoiceService.invoiceOnChanged // Set the private defaults
this._unsubscribeAll = new Subject();
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
this._invoiceService.invoiceOnChanged
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((invoice) => { .subscribe((invoice) => {
this.invoice = invoice; this.invoice = invoice;
}); });
} }
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
} }

View File

@ -3,13 +3,13 @@ import { RouterModule } from '@angular/router';
import { FuseSharedModule } from '@fuse/shared.module'; import { FuseSharedModule } from '@fuse/shared.module';
import { InvoiceService } from '../invoice.service'; import { InvoiceService } from 'app/main/pages/invoices/invoice.service';
import { FuseInvoiceModernComponent } from './modern.component'; import { InvoiceModernComponent } from 'app/main/pages/invoices/modern/modern.component';
const routes = [ const routes = [
{ {
path : 'invoices/modern', path : 'invoices/modern',
component: FuseInvoiceModernComponent, component: InvoiceModernComponent,
resolve : { resolve : {
search: InvoiceService search: InvoiceService
} }
@ -18,7 +18,7 @@ const routes = [
@NgModule({ @NgModule({
declarations: [ declarations: [
FuseInvoiceModernComponent InvoiceModernComponent
], ],
imports : [ imports : [
RouterModule.forChild(routes), RouterModule.forChild(routes),

View File

@ -2,16 +2,16 @@
<mat-toolbar matDialogTitle class="mat-accent m-0"> <mat-toolbar matDialogTitle class="mat-accent m-0">
<mat-toolbar-row> <mat-toolbar-row>
<span class="title dialog-title">{{data.article.title}}</span> <span class="title dialog-title">{{_data.article.title}}</span>
</mat-toolbar-row> </mat-toolbar-row>
</mat-toolbar> </mat-toolbar>
<div mat-dialog-content class="p-24 m-0" fusePerfectScrollbar> <div mat-dialog-content class="p-24 m-0" fusePerfectScrollbar>
<div [innerHTML]="data.article.content"></div> <div [innerHTML]="_data.article.content"></div>
</div> </div>
<div mat-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="end center"> <div mat-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="end center">
<button mat-button (click)="dialogRef.close()" class="mat-accent" aria-label="Close"> <button mat-button (click)="_matDialogRef.close()" class="mat-accent" aria-label="Close">
CLOSE CLOSE
</button> </button>
</div> </div>

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