(Calendar App) Calendar app added.

This commit is contained in:
mustafahlvc 2017-07-26 10:02:26 +03:00
parent 0270405353
commit ed06e26647
22 changed files with 890 additions and 175 deletions

5
package-lock.json generated
View File

@ -5929,6 +5929,11 @@
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
"dev": true "dev": true
}, },
"ngx-color-picker": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-4.2.0.tgz",
"integrity": "sha512-xWFpvOc+0WOD2kppPDlN1q5p58jgQDgUSsier/xi1i0HaVuU+BgNCo7aFPAKHaovw0Gv1WWp5GPAdpjXdUe7KA=="
},
"ngx-perfect-scrollbar": { "ngx-perfect-scrollbar": {
"version": "4.5.2", "version": "4.5.2",
"resolved": "https://registry.npmjs.org/ngx-perfect-scrollbar/-/ngx-perfect-scrollbar-4.5.2.tgz", "resolved": "https://registry.npmjs.org/ngx-perfect-scrollbar/-/ngx-perfect-scrollbar-4.5.2.tgz",

View File

@ -29,6 +29,7 @@
"core-js": "^2.4.1", "core-js": "^2.4.1",
"firebase": "^4.1.3", "firebase": "^4.1.3",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"ngx-color-picker": "^4.2.0",
"ngx-perfect-scrollbar": "^4.5.2", "ngx-perfect-scrollbar": "^4.5.2",
"rxjs": "^5.4.2", "rxjs": "^5.4.2",
"zone.js": "^0.8.13" "zone.js": "^0.8.13"

View File

@ -0,0 +1,6 @@
<h1 md-dialog-title>Confirm</h1>
<div md-dialog-content>{{confirmMessage}}</div>
<div md-dialog-actions class="pt-24">
<button md-raised-button class="mat-accent mr-16" (click)="dialogRef.close(true)">Confirm</button>
<button md-button (click)="dialogRef.close(false)">Cancel</button>
</div>

View File

@ -0,0 +1,21 @@
import { Component, OnInit } from '@angular/core';
import { MdDialogRef } from '@angular/material';
@Component({
selector : 'fuse-confirm-dialog',
templateUrl: './confirm-dialog.component.html',
styleUrls : ['./confirm-dialog.component.scss']
})
export class FuseConfirmDialogComponent implements OnInit
{
public confirmMessage: string;
constructor(public dialogRef: MdDialogRef<FuseConfirmDialogComponent>)
{
}
ngOnInit()
{
}
}

View File

@ -11,12 +11,14 @@ import {
FuseMdSidenavTogglerDirective FuseMdSidenavTogglerDirective
} from '../directives/md-sidenav-helper/md-sidenav-helper.directive'; } from '../directives/md-sidenav-helper/md-sidenav-helper.directive';
import { FusePipesModule } from '../pipes/pipes.module'; import { FusePipesModule } from '../pipes/pipes.module';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ColorPickerModule } from 'ngx-color-picker';
import { FuseConfirmDialogComponent } from '../components/confirm-dialog/confirm-dialog.component';
@NgModule({ @NgModule({
declarations: [ declarations : [
FuseMdSidenavHelperDirective, FuseMdSidenavHelperDirective,
FuseMdSidenavTogglerDirective FuseMdSidenavTogglerDirective,
FuseConfirmDialogComponent
], ],
imports : [ imports : [
FlexLayoutModule, FlexLayoutModule,
@ -25,7 +27,8 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
FormsModule, FormsModule,
FusePipesModule, FusePipesModule,
PerfectScrollbarModule, PerfectScrollbarModule,
ReactiveFormsModule ReactiveFormsModule,
ColorPickerModule
], ],
exports : [ exports : [
FlexLayoutModule, FlexLayoutModule,
@ -36,8 +39,10 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
FuseMdSidenavTogglerDirective, FuseMdSidenavTogglerDirective,
FusePipesModule, FusePipesModule,
PerfectScrollbarModule, PerfectScrollbarModule,
ReactiveFormsModule ReactiveFormsModule,
] ColorPickerModule
],
entryComponents: [FuseConfirmDialogComponent]
}) })
export class SharedModule export class SharedModule

View File

@ -0,0 +1,97 @@
import {
startOfDay,
endOfDay,
subDays,
addDays,
endOfMonth,
isSameDay,
isSameMonth,
addHours
} from 'date-fns';
export class CalendarFakeDb
{
public static data = [
{
id : 'events',
data: [
{
start : subDays(startOfDay(new Date()), 1),
end : addDays(new Date(), 1),
title : 'A 3 day event',
allDay : false,
color : {
primary : '#ad2121',
secondary: '#FAE3E3'
},
resizable: {
beforeStart: true,
afterEnd : true
},
draggable: true,
meta : {
location: 'Los Angeles',
notes : 'Eos eu verear adipiscing, ex ornatus denique iracundia sed, quodsi oportere appellantur an pri.'
}
},
{
start : startOfDay(new Date()),
title : 'An event with no end date',
allDay : false,
color : {
primary : '#e3bc08',
secondary: '#FDF1BA'
},
resizable: {
beforeStart: true,
afterEnd : true
},
draggable: true,
meta : {
location: 'Los Angeles',
notes : 'Eos eu verear adipiscing, ex ornatus denique iracundia sed, quodsi oportere appellantur an pri.'
}
},
{
start : subDays(endOfMonth(new Date()), 3),
end : addDays(endOfMonth(new Date()), 3),
title : 'A long event that spans 2 months',
allDay : false,
color : {
primary : '#1e90ff',
secondary: '#D1E8FF'
},
resizable: {
beforeStart: true,
afterEnd : true
},
draggable: true,
meta : {
location: 'Los Angeles',
notes : 'Eos eu verear adipiscing, ex ornatus denique iracundia sed, quodsi oportere appellantur an pri.'
}
},
{
start : addHours(startOfDay(new Date()), 2),
end : new Date(),
title : 'A draggable and resizable event',
allDay : false,
color : {
primary : '#e3bc08',
secondary: '#FDF1BA'
},
resizable: {
beforeStart: true,
afterEnd : true
},
draggable: true,
meta : {
location: 'Los Angeles',
notes : 'Eos eu verear adipiscing, ex ornatus denique iracundia sed, quodsi oportere appellantur an pri.'
}
}
]
}
];
}

View File

@ -1,6 +1,17 @@
import { InMemoryDbService } from 'angular-in-memory-web-api'; import { InMemoryDbService } from 'angular-in-memory-web-api';
import { MailFakeDb } from './mail'; import { MailFakeDb } from './mail';
import {ChatFakeDb} from './chat'; import { ChatFakeDb } from './chat';
import { CalendarFakeDb } from './calendar';
import {
startOfDay,
endOfDay,
subDays,
addDays,
endOfMonth,
isSameDay,
isSameMonth,
addHours
} from 'date-fns';
export class FuseFakeDbService implements InMemoryDbService export class FuseFakeDbService implements InMemoryDbService
{ {
@ -8,12 +19,13 @@ export class FuseFakeDbService implements InMemoryDbService
{ {
return { return {
'mail-mails' : MailFakeDb.mails, 'mail-mails' : MailFakeDb.mails,
'mail-folders': MailFakeDb.folders, 'mail-folders' : MailFakeDb.folders,
'mail-filters': MailFakeDb.filters, 'mail-filters' : MailFakeDb.filters,
'mail-labels' : MailFakeDb.labels, 'mail-labels' : MailFakeDb.labels,
'chat-contacts': ChatFakeDb.contacts, 'chat-contacts': ChatFakeDb.contacts,
'chat-chats' : ChatFakeDb.chats, 'chat-chats' : ChatFakeDb.chats,
'chat-user' : ChatFakeDb.user, 'chat-user' : ChatFakeDb.user,
'calendar' : CalendarFakeDb.data
}; };
} }

View File

@ -25,7 +25,9 @@
<button md-button class="mat-icon-button" <button md-button class="mat-icon-button"
mwlCalendarToday mwlCalendarToday
[(viewDate)]="viewDate" [(viewDate)]="viewDate"
(viewDateChange)="selectedDay = {date:$event}"
aria-label="Today" md-tooltip="today"> aria-label="Today" md-tooltip="today">
<!--(click)="selectedDay = viewDate"-->
<md-icon>today</md-icon> <md-icon>today</md-icon>
</button> </button>
@ -54,19 +56,20 @@
mwlCalendarPreviousView mwlCalendarPreviousView
[view]="view" [view]="view"
[(viewDate)]="viewDate" [(viewDate)]="viewDate"
(viewDateChange)="selectedDay = {date:$event}"
aria-label="Previous"> aria-label="Previous">
<md-icon>chevron_left</md-icon> <md-icon>chevron_left</md-icon>
</button> </button>
<div class="title"> <div class="title">
<h3>{{ viewDate | calendarDate:(view + 'ViewTitle'):'en' }}</h3> {{ viewDate | calendarDate:(view + 'ViewTitle'):'en' }}
<!--{{calendarView.title}}-->
</div> </div>
<button md-button class="mat-icon-button arrow" <button md-button class="mat-icon-button arrow"
mwlCalendarNextView mwlCalendarNextView
[view]="view" [view]="view"
[(viewDate)]="viewDate" [(viewDate)]="viewDate"
(viewDateChange)="selectedDay = {date:$event}"
aria-label="Next"> aria-label="Next">
<md-icon>chevron_right</md-icon> <md-icon>chevron_right</md-icon>
</button> </button>
@ -92,23 +95,28 @@
[refresh]="refresh" [refresh]="refresh"
[activeDayIsOpen]="activeDayIsOpen" [activeDayIsOpen]="activeDayIsOpen"
(dayClicked)="dayClicked($event.day)" (dayClicked)="dayClicked($event.day)"
(eventClicked)="handleEvent('Clicked', $event.event)" (eventClicked)="editEvent('edit', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)"> (eventTimesChanged)="eventTimesChanged($event)"
(beforeViewRender)="beforeMonthViewRender($event)">
</mwl-calendar-month-view> </mwl-calendar-month-view>
<mwl-calendar-week-view <mwl-calendar-week-view
*ngSwitchCase="'week'" *ngSwitchCase="'week'"
[viewDate]="viewDate" [viewDate]="viewDate"
(viewDateChange)="selectedDay = {date:$event}"
[events]="events" [events]="events"
[refresh]="refresh" [refresh]="refresh"
(eventClicked)="handleEvent('Clicked', $event.event)" (dayClicked)="dayClicked($event.day)"
(eventClicked)="editEvent('edit', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)"> (eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-week-view> </mwl-calendar-week-view>
<mwl-calendar-day-view <mwl-calendar-day-view
*ngSwitchCase="'day'" *ngSwitchCase="'day'"
[viewDate]="viewDate" [viewDate]="viewDate"
(viewDateChange)="selectedDay = {date:$event}"
[events]="events" [events]="events"
[refresh]="refresh" [refresh]="refresh"
(eventClicked)="handleEvent('Clicked', $event.event)" (dayClicked)="dayClicked($event.day)"
(eventClicked)="editEvent('edit', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)"> (eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-day-view> </mwl-calendar-day-view>
</div> </div>

View File

@ -1,5 +1,179 @@
@import "src/app/core/scss/fuse";
@import "node_modules/angular-calendar/scss/angular-calendar"; @import "node_modules/angular-calendar/scss/angular-calendar";
.cal-month-view {
.cal-header {
.cal-cell {
font-weight: 500;
}
}
.cal-day-cell {
&.cal-open {
@include mat-elevation(3);
}
}
.cal-open-day-events {
background: whitesmoke;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.13);
padding: 0;
display: flex;
flex-direction: column;
> div {
padding: 0 16px;
margin: 8px 16px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: start;
background-color: white;
@include mat-elevation(1);
transition: box-shadow 300ms ease;
&:first-of-type {
margin-top: 24px;
}
&:last-of-type {
margin-bottom: 24px;
}
&:hover {
@include mat-elevation(3);
}
.cal-event {
top: 0;
margin: 0;
}
mwl-calendar-event-title {
flex: 1;
.cal-event-title {
display: block;
padding: 21px 24px;
line-height: 1;
text-decoration: none;
color: black;
}
}
mwl-calendar-event-actions {
.cal-event-actions {
display: flex;
flex-direction: row;
align-items: center;
.cal-event-action {
display: block;
line-height: 1;
padding: 8px;
}
}
}
}
}
}
.cal-week-view {
.cal-header > b {
font-weight: 500;
}
.cal-event {
display: flex;
flex-direction: row;
align-items: center;
mwl-calendar-event-title {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
order: 0;
.cal-event-title {
display: block;
//padding: 21px 24px;
line-height: 1;
text-decoration: none;
color: black;
}
}
mwl-calendar-event-actions {
order: 1;
.cal-event-actions {
display: flex;
flex-direction: row;
align-items: center;
.cal-event-action {
display: block;
line-height: 1;
padding: 8px;
}
}
}
}
}
.cal-day-view {
.cal-time {
font-weight: 500;
}
.cal-event {
display: flex;
flex-direction: row;
mwl-calendar-event-title {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
order: 0;
.cal-event-title {
display: block;
height: 26px;
line-height: 26px;
text-decoration: none;
color: black;
}
}
mwl-calendar-event-actions {
order: 1;
.cal-event-actions {
display: flex;
flex-direction: row;
align-items: center;
.cal-event-action {
display: block;
line-height: 1;
padding: 4px;
}
}
}
}
}
#calendar { #calendar {
background: #FFFFFF; background: #FFFFFF;
height: 100%; height: 100%;
@ -104,6 +278,7 @@
font-size: 20px; font-size: 20px;
min-width: 160px; min-width: 160px;
text-align: center; text-align: center;
font-weight: 500;
} }
} }
} }
@ -121,6 +296,8 @@
} }
.content { .content {
flex: 1;
overflow: auto;
padding: 24px; padding: 24px;
} }
} }

View File

@ -1,41 +1,18 @@
import { import { startOfDay, endOfDay, subDays, addDays, endOfMonth, isSameDay, isSameMonth, addHours } from 'date-fns';
ChangeDetectionStrategy, import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core';
Component,
Inject,
OnInit,
TemplateRef,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { Subject } from 'rxjs/Subject'; import { Subject } from 'rxjs/Subject';
import { CalendarEvent, CalendarEventAction, CalendarEventTimesChangedEvent } from 'angular-calendar'; import { MdDialog, MdDialogRef } from '@angular/material';
import { MD_DIALOG_DATA, MdDialog, MdDialogRef } from '@angular/material'; import { EventFormDialogComponent } from './event-form/event-form.component';
import { EventDialogComponent } from './event-dialog/event-dialog.component'; import { FormGroup } from '@angular/forms';
import { CalendarEventModel } from './event.model';
import { CalendarService } from './calendar.service';
import { import {
startOfDay, CalendarEvent,
endOfDay, CalendarEventAction,
subDays, CalendarEventTimesChangedEvent,
addDays, CalendarMonthViewDay
endOfMonth, } from 'angular-calendar';
isSameDay, import { FuseConfirmDialogComponent } from '../../../core/components/confirm-dialog/confirm-dialog.component';
isSameMonth,
addHours
} from 'date-fns';
const colors: any = {
red : {
primary : '#ad2121',
secondary: '#FAE3E3'
},
blue : {
primary : '#1e90ff',
secondary: '#D1E8FF'
},
yellow: {
primary : '#e3bc08',
secondary: '#FDF1BA'
}
};
@Component({ @Component({
selector : 'fuse-calendar', selector : 'fuse-calendar',
@ -54,7 +31,7 @@ export class CalendarComponent implements OnInit
events: CalendarEvent[]; events: CalendarEvent[];
actions: CalendarEventAction[]; public actions: CalendarEventAction[];
activeDayIsOpen: boolean; activeDayIsOpen: boolean;
@ -62,75 +39,106 @@ export class CalendarComponent implements OnInit
dialogRef: any; dialogRef: any;
constructor(public dialog: MdDialog) confirmDialogRef: MdDialogRef<FuseConfirmDialogComponent>;
selectedDay: any;
constructor(public dialog: MdDialog,
public calendarService: CalendarService)
{ {
this.view = 'month'; this.view = 'month';
this.viewDate = new Date(); this.viewDate = new Date();
this.activeDayIsOpen = true; this.activeDayIsOpen = true;
this.selectedDay = {date: startOfDay(new Date())};
this.actions = [ this.actions = [
{ {
label : '<i class="material-icons s-16">edit</i>', label : '<i class="material-icons s-16">edit</i>',
onClick: ({event}: { event: CalendarEvent }): void => { onClick: ({event}: { event: CalendarEvent }): void => {
this.handleEvent('Edited', event); this.editEvent('edit', event);
} }
}, },
{ {
label : '<i class="material-icons s-16">close</i>', label : '<i class="material-icons s-16">delete</i>',
onClick: ({event}: { event: CalendarEvent }): void => { onClick: ({event}: { event: CalendarEvent }): void => {
this.events = this.events.filter(iEvent => iEvent !== event); this.deleteEvent(event);
this.handleEvent('Deleted', event);
} }
} }
]; ];
this.events = [ /**
{ * Get events from service/server
start : subDays(startOfDay(new Date()), 1), */
end : addDays(new Date(), 1), this.setEvents();
title : 'A 3 day event',
color : colors.red,
actions: this.actions
},
{
start : startOfDay(new Date()),
title : 'An event with no end date',
color : colors.yellow,
actions: this.actions
},
{
start: subDays(endOfMonth(new Date()), 3),
end : addDays(endOfMonth(new Date()), 3),
title: 'A long event that spans 2 months',
color: colors.blue
},
{
start : addHours(startOfDay(new Date()), 2),
end : new Date(),
title : 'A draggable and resizable event',
color : colors.yellow,
actions : this.actions,
resizable: {
beforeStart: true,
afterEnd : true
},
draggable: true
}
];
} }
ngOnInit() ngOnInit()
{ {
/**
* Watch re-render-refresh for updating db
*/
this.refresh.subscribe(updateDB => {
// console.warn('REFRESH');
if ( updateDB )
{
// console.warn('UPDATE DB');
this.calendarService.updateEvents(this.events);
}
});
this.calendarService.onEventsUpdated.subscribe(events => {
this.setEvents();
this.refresh.next();
});
} }
dayClicked({date, events}: { date: Date; events: CalendarEvent[] }): void setEvents()
{ {
this.events = this.calendarService.events.map(item => {
item.actions = this.actions;
return new CalendarEventModel(item);
});
}
/**
* Before View Renderer
* @param {any} header
* @param {any} body
*/
beforeMonthViewRender({header, body})
{
// console.info('beforeMonthViewRender');
/**
* Get the selected day
*/
const _selectedDay = body.find((_day) => {
return _day.date.getTime() === this.selectedDay.date.getTime();
});
if ( _selectedDay )
{
/**
* Set selectedday style
* @type {string}
*/
_selectedDay.cssClass = 'mat-elevation-z3';
}
}
/**
* Day clicked
* @param {MonthViewDay} day
*/
dayClicked(day: CalendarMonthViewDay): void
{
const date: Date = day.date;
const events: CalendarEvent[] = day.events;
if ( isSameMonth(date, this.viewDate) ) if ( isSameMonth(date, this.viewDate) )
{ {
if ( if ( (isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) || events.length === 0 )
(isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) ||
events.length === 0
)
{ {
this.activeDayIsOpen = false; this.activeDayIsOpen = false;
} }
@ -140,48 +148,120 @@ export class CalendarComponent implements OnInit
this.viewDate = date; this.viewDate = date;
} }
} }
} this.selectedDay = day;
eventTimesChanged({
event,
newStart,
newEnd
}: CalendarEventTimesChangedEvent): void
{
event.start = newStart;
event.end = newEnd;
this.handleEvent('Dropped or resized', event);
this.refresh.next(); this.refresh.next();
} }
handleEvent(action: string, event: CalendarEvent): void /**
* Event times changed
* Event dropped or resized
* @param {CalendarEvent} event
* @param {Date} newStart
* @param {Date} newEnd
*/
eventTimesChanged({event, newStart, newEnd}: CalendarEventTimesChangedEvent): void
{ {
console.log(event, action); event.start = newStart;
this.dialogRef = this.dialog.open(EventDialogComponent, { event.end = newEnd;
data: { // console.warn('Dropped or resized', event);
this.refresh.next(true);
}
/**
* Delete Event
* @param event
*/
deleteEvent(event)
{
this.confirmDialogRef = this.dialog.open(FuseConfirmDialogComponent, {
disableClose: false
});
this.confirmDialogRef.componentInstance.confirmMessage = 'Are you sure you want to delete?';
this.confirmDialogRef.afterClosed().subscribe(result => {
if ( result )
{
const eventIndex = this.events.indexOf(event);
this.events.splice(eventIndex, 1);
this.refresh.next(true);
}
this.confirmDialogRef = null;
});
}
/**
* Edit Event
* @param {string} action
* @param {CalendarEvent} event
*/
editEvent(action: string, event: CalendarEvent)
{
const eventIndex = this.events.indexOf(event);
this.dialogRef = this.dialog.open(EventFormDialogComponent, {
panelClass: 'event-form-dialog',
data : {
event : event, event : event,
action: action action: action
} }
}); });
this.dialogRef.afterClosed().subscribe(result => {
console.info(result); this.dialogRef.afterClosed()
.subscribe(response => {
if ( !response )
{
return;
}
const actionType: string = response[0];
const formData: FormGroup = response[1];
switch ( actionType )
{
/**
* Save
*/
case 'save':
this.events[eventIndex] = Object.assign(this.events[eventIndex], formData.getRawValue());
this.refresh.next(true);
break;
/**
* Delete
*/
case 'delete':
this.deleteEvent(event);
break;
}
}); });
} }
/**
* Add Event
*/
addEvent(): void addEvent(): void
{ {
this.events.push({ this.dialogRef = this.dialog.open(EventFormDialogComponent, {
title : 'New event', panelClass: 'event-form-dialog',
start : startOfDay(new Date()), data : {
end : endOfDay(new Date()), action: 'new',
color : colors.red, date : this.selectedDay.date
draggable: true,
resizable: {
beforeStart: true,
afterEnd : true
} }
}); });
this.refresh.next(); this.dialogRef.afterClosed()
.subscribe((response: FormGroup) => {
if ( !response )
{
return;
}
const newEvent = response.getRawValue();
newEvent.actions = this.actions;
this.events.push(newEvent);
this.refresh.next(true);
});
} }
} }

View File

@ -4,7 +4,8 @@ import { RouterModule, Routes } from '@angular/router';
import { CalendarComponent } from './calendar.component'; import { CalendarComponent } from './calendar.component';
import { CalendarService } from './calendar.service'; import { CalendarService } from './calendar.service';
import { CalendarModule } from 'angular-calendar'; import { CalendarModule } from 'angular-calendar';
import { EventDialogComponent } from './event-dialog/event-dialog.component'; import { EventFormDialogComponent } from './event-form/event-form.component';
import { EventDetailDialogComponent } from './event-detail/event-detail.component';
const routes: Routes = [ const routes: Routes = [
{ {
@ -23,12 +24,13 @@ const routes: Routes = [
], ],
declarations : [ declarations : [
CalendarComponent, CalendarComponent,
EventDialogComponent, EventFormDialogComponent,
EventDetailDialogComponent,
], ],
providers : [ providers : [
CalendarService CalendarService
], ],
entryComponents: [EventDialogComponent] entryComponents: [EventFormDialogComponent, EventDetailDialogComponent]
}) })
export class FuseCalendarModule export class FuseCalendarModule
{ {

View File

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

View File

@ -0,0 +1,3 @@
<p>
event-detail works!
</p>

View File

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

View File

@ -1,20 +0,0 @@
<h1 md-dialog-title>
<h5 class="modal-title">Event action occurred</h5>
<button type="button" class="close" (click)="dialogRef.close()">
<span aria-hidden="true">&times;</span>
</button>
</h1>
<div md-dialog-content>
<div>
Action:
<pre>{{ data.action }}</pre>
</div>
<div>
Event:
<pre>{{ data.event | json }}</pre>
</div>
</div>
<div md-dialog-actions>
<button md-button class="dialog-button" (click)="dialogRef.close('Option 1')">OPTION 1</button>
<button md-button class="dialog-button" (click)="dialogRef.close('Option 2')">OPTION 2</button>
</div>

View File

@ -1,22 +0,0 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MD_DIALOG_DATA, MdDialogRef } from '@angular/material';
@Component({
selector : 'fuse-calendar-event-dialog',
templateUrl: './event-dialog.component.html',
styleUrls : ['./event-dialog.component.scss']
})
export class EventDialogComponent implements OnInit
{
constructor(private dialogRef: MdDialogRef<EventDialogComponent>,
@Inject(MD_DIALOG_DATA) private data: any)
{
console.log(data);
}
ngOnInit()
{
}
}

View File

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

View File

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

View File

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

View File

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