From 56e6b854c287d2fd5e2fa95f7fba40ba18a8d1be Mon Sep 17 00:00:00 2001 From: mustafahlvc Date: Sun, 23 Jul 2017 10:32:13 +0300 Subject: [PATCH 1/5] Filter pipe added, spacing class helpers added(margin, padding), Normalize css added, Icon size class helpers added, slideInLeft, slideInRight animation added, Chat app almost completed. --- src/app/app.module.ts | 5 + src/app/core/animations.ts | 14 +- src/app/core/modules/shared.module.ts | 8 +- src/app/core/pipes/filter.pipe.ts | 152 ++++++ src/app/core/pipes/pipes.module.ts | 8 +- src/app/core/scss/core.scss | 6 +- .../scss/partials/_angular-material-fix.scss | 2 +- src/app/core/scss/partials/_forms.scss | 15 + src/app/core/scss/partials/_global.scss | 6 +- src/app/core/scss/partials/_icons.scss | 22 + src/app/core/scss/partials/_material.scss | 48 ++ src/app/core/scss/partials/_normalize.scss | 447 ++++++++++++++++++ src/app/core/scss/partials/_spacing.scss | 62 +++ src/app/fuse-fake-db/chat.ts | 325 +++++++++++++ src/app/fuse-fake-db/fuse-fake-db.service.ts | 8 +- .../chat/chat-start/chat-start.component.html | 17 + .../chat/chat-start/chat-start.component.scss | 30 ++ .../chat/chat-start/chat-start.component.ts | 19 + .../chat/chat-view/chat-view.component.html | 115 +++++ .../chat/chat-view/chat-view.component.scss | 135 ++++++ .../chat/chat-view/chat-view.component.ts | 101 ++++ src/app/main/apps/chat/chat.component.html | 46 +- src/app/main/apps/chat/chat.component.scss | 31 ++ src/app/main/apps/chat/chat.component.ts | 26 +- src/app/main/apps/chat/chat.module.ts | 25 +- src/app/main/apps/chat/chat.service.ts | 259 ++++++++++ .../sidenavs/left/chats/chats.component.html | 183 +++++++ .../sidenavs/left/chats/chats.component.scss | 110 +++++ .../sidenavs/left/chats/chats.component.ts | 66 +++ .../chat/sidenavs/left/left.component.html | 11 + .../chat/sidenavs/left/left.component.scss | 20 + .../apps/chat/sidenavs/left/left.component.ts | 27 ++ .../sidenavs/left/user/user.component.html | 78 +++ .../sidenavs/left/user/user.component.scss | 21 + .../chat/sidenavs/left/user/user.component.ts | 47 ++ .../right/contact/contact.component.html | 47 ++ .../right/contact/contact.component.scss | 21 + .../right/contact/contact.component.ts | 25 + .../chat/sidenavs/right/right.component.html | 8 + .../chat/sidenavs/right/right.component.scss | 20 + .../chat/sidenavs/right/right.component.ts | 27 ++ tslint.json | 2 +- 42 files changed, 2620 insertions(+), 25 deletions(-) create mode 100644 src/app/core/pipes/filter.pipe.ts create mode 100644 src/app/core/scss/partials/_forms.scss create mode 100644 src/app/core/scss/partials/_icons.scss create mode 100644 src/app/core/scss/partials/_normalize.scss create mode 100644 src/app/core/scss/partials/_spacing.scss create mode 100644 src/app/fuse-fake-db/chat.ts create mode 100644 src/app/main/apps/chat/chat-start/chat-start.component.html create mode 100644 src/app/main/apps/chat/chat-start/chat-start.component.scss create mode 100644 src/app/main/apps/chat/chat-start/chat-start.component.ts create mode 100644 src/app/main/apps/chat/chat-view/chat-view.component.html create mode 100644 src/app/main/apps/chat/chat-view/chat-view.component.scss create mode 100644 src/app/main/apps/chat/chat-view/chat-view.component.ts create mode 100644 src/app/main/apps/chat/chat.service.ts create mode 100644 src/app/main/apps/chat/sidenavs/left/chats/chats.component.html create mode 100644 src/app/main/apps/chat/sidenavs/left/chats/chats.component.scss create mode 100644 src/app/main/apps/chat/sidenavs/left/chats/chats.component.ts create mode 100644 src/app/main/apps/chat/sidenavs/left/left.component.html create mode 100644 src/app/main/apps/chat/sidenavs/left/left.component.scss create mode 100644 src/app/main/apps/chat/sidenavs/left/left.component.ts create mode 100644 src/app/main/apps/chat/sidenavs/left/user/user.component.html create mode 100644 src/app/main/apps/chat/sidenavs/left/user/user.component.scss create mode 100644 src/app/main/apps/chat/sidenavs/left/user/user.component.ts create mode 100644 src/app/main/apps/chat/sidenavs/right/contact/contact.component.html create mode 100644 src/app/main/apps/chat/sidenavs/right/contact/contact.component.scss create mode 100644 src/app/main/apps/chat/sidenavs/right/contact/contact.component.ts create mode 100644 src/app/main/apps/chat/sidenavs/right/right.component.html create mode 100644 src/app/main/apps/chat/sidenavs/right/right.component.scss create mode 100644 src/app/main/apps/chat/sidenavs/right/right.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4a69a6e5..6ab85110 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -20,6 +20,7 @@ import { PerfectScrollbarConfigInterface, PerfectScrollbarModule } from 'ngx-per import { HttpModule } from '@angular/http'; import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; import { FuseFakeDbService } from './fuse-fake-db/fuse-fake-db.service'; +import { INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS } from '@angular/platform-browser-dynamic/src/platform_providers'; const PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = { suppressScrollX: true @@ -30,6 +31,10 @@ const appRoutes: Routes = [ path : 'apps/mail', loadChildren: './main/apps/mail/mail.module#MailModule' }, + { + path : 'apps/chat', + loadChildren: './main/apps/chat/chat.module#ChatModule' + }, { path : '**', redirectTo: 'apps/dashboards/project' diff --git a/src/app/core/animations.ts b/src/app/core/animations.ts index ebb2429b..f140d216 100644 --- a/src/app/core/animations.ts +++ b/src/app/core/animations.ts @@ -1,4 +1,4 @@ -import {trigger, state, transition, animate, style} from '@angular/animations'; +import { trigger, state, transition, animate, style } from '@angular/animations'; export class Animations { @@ -8,4 +8,16 @@ export class Animations transition('1 => 0', animate('300ms ease-out')), transition('0 => 1', animate('300ms ease-in')) ]); + public static slideInLeft = trigger('slideInLeft', [ + state('void', style({transform: 'translateX(-100%)', display: 'none'})), + state('*', style({transform: 'translateX(0)', display: 'flex'})), + transition('void => *', animate('300ms')), + transition('* => void', animate('300ms')) + ]); + public static slideInRight = trigger('slideInRight', [ + state('void', style({transform: 'translateX(100%)', display: 'none'})), + state('*', style({transform: 'translateX(0)', display: 'flex'})), + transition('void => *', animate('300ms')), + transition('* => void', animate('300ms')) + ]); } diff --git a/src/app/core/modules/shared.module.ts b/src/app/core/modules/shared.module.ts index 66d60ba4..2b19e199 100644 --- a/src/app/core/modules/shared.module.ts +++ b/src/app/core/modules/shared.module.ts @@ -1,5 +1,5 @@ import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { CommonModule } from '@angular/common'; import { MaterialModule } from './material.module'; @@ -23,7 +23,8 @@ import { FusePipesModule } from '../pipes/pipes.module'; CommonModule, FormsModule, FusePipesModule, - PerfectScrollbarModule + PerfectScrollbarModule, + ReactiveFormsModule ], exports : [ FlexLayoutModule, @@ -33,7 +34,8 @@ import { FusePipesModule } from '../pipes/pipes.module'; FuseMdSidenavHelperDirective, FuseMdSidenavTogglerDirective, FusePipesModule, - PerfectScrollbarModule + PerfectScrollbarModule, + ReactiveFormsModule ] }) diff --git a/src/app/core/pipes/filter.pipe.ts b/src/app/core/pipes/filter.pipe.ts new file mode 100644 index 00000000..bc8835b0 --- /dev/null +++ b/src/app/core/pipes/filter.pipe.ts @@ -0,0 +1,152 @@ +/** + * Created by vadimdez on 28/06/16. + */ +import { Pipe, Injectable } from '@angular/core'; + +@Pipe({ + name: 'filterBy', + pure: false +}) + +@Injectable() +export class FilterPipe { + + private filterByString(filter) { + if (filter) { + filter = filter.toLowerCase(); + } + return value => { + return !filter || (value ? ('' + value).toLowerCase().indexOf(filter) !== -1 : false); + } + } + + private filterByBoolean(filter) { + return value => { + return Boolean(value) === filter; + } + } + + private filterByObject(filter) { + return value => { + for (let key in filter) { + + if (key === '$or') { + if (!this.filterByOr(filter.$or)(this.getValue(value))) { + return false; + } + continue; + } + + if (!value.hasOwnProperty(key) && !Object.getOwnPropertyDescriptor(Object.getPrototypeOf(value), key)) { + return false; + } + + let val = this.getValue(value[key]); + const filterType = typeof filter[key]; + let isMatching; + + if (filterType === 'boolean') { + isMatching = this.filterByBoolean(filter[key])(val); + } else if (filterType === 'string') { + isMatching = this.filterByString(filter[key])(val); + } else if (filterType === 'object') { + isMatching = this.filterByObject(filter[key])(val); + } else { + isMatching = this.filterDefault(filter[key])(val); + } + + if (!isMatching) { + return false; + } + } + + return true; + } + } + + /** + * Filter value by $or + * + * @param filter + * @returns {(value:any)=>boolean} + */ + private filterByOr(filter: any[]) { + return (value: any) => { + let hasMatch = false; + const length = filter.length; + const isArray = value instanceof Array; + + const arrayComparison = (i) => { + return value.indexOf(filter[i]) !== -1; + }; + const otherComparison = (i) => { + return value === filter[i]; + }; + const comparison = isArray ? arrayComparison : otherComparison; + + for (let i = 0; i < length; i++) { + if (comparison(i)) { + hasMatch = true; + break; + } + } + + return hasMatch; + }; + } + + /** + * Checks function's value if type is function otherwise same value + * @param value + * @returns {any} + */ + private getValue(value: any) { + return typeof value === 'function' ? value() : value; + } + + /** + * Defatul filterDefault function + * + * @param filter + * @returns {(value:any)=>boolean} + */ + private filterDefault(filter) { + return value => { + return filter === undefined || filter == value; + } + } + + private isNumber(value) { + return !isNaN(parseInt(value, 10)) && isFinite(value); + } + + transform(array: any[], filter: any): any { + const type = typeof filter; + + if (!array) { + return array; + } + + if (type === 'boolean') { + return array.filter(this.filterByBoolean(filter)); + } + + if (type === 'string') { + if (this.isNumber(filter)) { + return array.filter(this.filterDefault(filter)); + } + + return array.filter(this.filterByString(filter)); + } + + if (type === 'object') { + return array.filter(this.filterByObject(filter)); + } + + if (type === 'function') { + return array.filter(filter); + } + + return array.filter(this.filterDefault(filter)); + } +} diff --git a/src/app/core/pipes/pipes.module.ts b/src/app/core/pipes/pipes.module.ts index a5f01259..0a858c00 100644 --- a/src/app/core/pipes/pipes.module.ts +++ b/src/app/core/pipes/pipes.module.ts @@ -4,20 +4,24 @@ import { KeysPipe } from './keys.pipe'; import { GetByIdPipe } from './getById.pipe'; import { HtmlToPlaintextPipe } from './htmlToPlaintext.pipe'; import { SearchPipe } from './search.pipe'; +import { FilterPipe } from './filter.pipe'; @NgModule({ declarations: [ KeysPipe, GetByIdPipe, HtmlToPlaintextPipe, - SearchPipe + SearchPipe, + FilterPipe + ], imports : [], exports : [ KeysPipe, GetByIdPipe, HtmlToPlaintextPipe, - SearchPipe + SearchPipe, + FilterPipe ] }) diff --git a/src/app/core/scss/core.scss b/src/app/core/scss/core.scss index 3d09f236..7ca3040c 100644 --- a/src/app/core/scss/core.scss +++ b/src/app/core/scss/core.scss @@ -20,12 +20,16 @@ @include angular-material-typography($custom-typography); // Partials +@import "partials/normalize"; +@import "partials/spacing"; @import "partials/global"; -@import "partials/_material"; +@import "partials/icons"; +@import "partials/material"; @import "partials/angular-material-fix"; @import "partials/typography"; @import "partials/page-layouts"; @import "partials/navigation"; +@import "partials/forms"; // Plugins @import "partials/plugins/plugins"; diff --git a/src/app/core/scss/partials/_angular-material-fix.scss b/src/app/core/scss/partials/_angular-material-fix.scss index 8ddd8f91..f0ec4593 100644 --- a/src/app/core/scss/partials/_angular-material-fix.scss +++ b/src/app/core/scss/partials/_angular-material-fix.scss @@ -3,4 +3,4 @@ .mat-button-ripple { border-radius: 50%; } -} \ No newline at end of file +} diff --git a/src/app/core/scss/partials/_forms.scss b/src/app/core/scss/partials/_forms.scss new file mode 100644 index 00000000..2583493e --- /dev/null +++ b/src/app/core/scss/partials/_forms.scss @@ -0,0 +1,15 @@ +button, +input[type=email], +input[type=tel], +input[type=text], +input[type=password], +input[type=image], +input[type=submit], +input[type=button], +input[type=search], +textarea { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + outline: none; +} diff --git a/src/app/core/scss/partials/_global.scss b/src/app/core/scss/partials/_global.scss index 7f51a123..1426778e 100644 --- a/src/app/core/scss/partials/_global.scss +++ b/src/app/core/scss/partials/_global.scss @@ -2,6 +2,10 @@ box-sizing: border-box; } +html { + +} + html, body { margin: 0; width: 100%; @@ -21,4 +25,4 @@ body { > md-sidenav-container { height: 100%; } -} \ No newline at end of file +} diff --git a/src/app/core/scss/partials/_icons.scss b/src/app/core/scss/partials/_icons.scss new file mode 100644 index 00000000..d64fd37f --- /dev/null +++ b/src/app/core/scss/partials/_icons.scss @@ -0,0 +1,22 @@ +i, +md-icon { + color: rgba(0, 0, 0, 0.54); + font-size: 24px; + width: 24px; + height: 24px; + min-width: 24px; + min-height: 24px; + line-height: 24px; + + @for $size from 2 through 128 { + + &.s-#{$size * 2} { + font-size: #{($size * 2) + 'px'} !important; + width: #{($size * 2) + 'px'} !important; + height: #{($size * 2) + 'px'} !important; + min-width: #{($size * 2) + 'px'} !important; + min-height: #{($size * 2) + 'px'} !important; + line-height: #{($size * 2) + 'px'} !important; + } + } +} diff --git a/src/app/core/scss/partials/_material.scss b/src/app/core/scss/partials/_material.scss index 3ec5e82e..406d23f8 100644 --- a/src/app/core/scss/partials/_material.scss +++ b/src/app/core/scss/partials/_material.scss @@ -39,6 +39,54 @@ } } +.avatar-wrapper { + position: relative; + + .avatar { + margin-top: 0; + margin-bottom: 0; + } + md-icon.status { + position: absolute; + top: 28px; + left: 28px; + } +} + +md-icon.status { + border-radius: 50%; + + &.online { + color: #4CAF50; + &:before { + content: "check_circle"; + } + } + + &.do-not-disturb { + color: #F44336; + &:before { + content: "do_not_disturb_on"; + } + } + + &.away { + background-color: #FFC107; + color: #FFFFFF; + &:before { + content: "access_time"; + } + } + + &.offline { + color: #646464; + background-color: #FFFFFF; + &:before { + content: "not_interested"; + } + } +} + /*----------------------------------------------------------------*/ /* Forms /*----------------------------------------------------------------*/ diff --git a/src/app/core/scss/partials/_normalize.scss b/src/app/core/scss/partials/_normalize.scss new file mode 100644 index 00000000..6d95002c --- /dev/null +++ b/src/app/core/scss/partials/_normalize.scss @@ -0,0 +1,447 @@ +/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in + * IE on Windows Phone and in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers (opinionated). + */ + +body { + margin: 0; +} + +/** + * Add the correct display in IE 9-. + */ + +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + * 1. Add the correct display in IE. + */ + +figcaption, +figure, +main { /* 1 */ + display: block; +} + +/** + * Add the correct margin in IE 8. + */ + +figure { + margin: 1em 40px; +} + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * 1. Remove the gray background on active links in IE 10. + * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. + */ + +a { + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ +} + +/** + * 1. Remove the bottom border in Chrome 57- and Firefox 39-. + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Prevent the duplicate application of `bolder` by the next rule in Safari 6. + */ + +b, +strong { + font-weight: inherit; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font style in Android 4.3-. + */ + +dfn { + font-style: italic; +} + +/** + * Add the correct background and color in IE 9-. + */ + +mark { + background-color: #ff0; + color: #000; +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images inside links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers (opinionated). + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: sans-serif; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` + * controls in Android 4. + * 2. Correct the inability to style clickable types in iOS and Safari. + */ + +button, +html [type="button"], /* 1 */ +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 2 */ +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in IE 9-. + * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the default vertical scrollbar in IE. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in IE 9-. + * 1. Add the correct display in Edge, IE, and Firefox. + */ + +details, /* 1 */ +menu { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Scripting + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* Hidden + ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +} diff --git a/src/app/core/scss/partials/_spacing.scss b/src/app/core/scss/partials/_spacing.scss new file mode 100644 index 00000000..6da1c3ef --- /dev/null +++ b/src/app/core/scss/partials/_spacing.scss @@ -0,0 +1,62 @@ +// Margin and Padding +@each $prop, $abbrev in (margin: m, padding: p) { + + @for $index from 0 through 64 { + $size: $index *4; + $length: #{$size}px; + .#{$abbrev}-#{$size} { + #{$prop}: $length !important; + } + .#{$abbrev}t-#{$size} { + #{$prop}-top: $length !important; + } + .#{$abbrev}r-#{$size} { + #{$prop}-right: $length !important; + } + .#{$abbrev}b-#{$size} { + #{$prop}-bottom: $length !important; + } + .#{$abbrev}l-#{$size} { + #{$prop}-left: $length !important; + } + .#{$abbrev}x-#{$size} { + #{$prop}-right: $length !important; + #{$prop}-left: $length !important; + } + .#{$abbrev}y-#{$size} { + #{$prop}-top: $length !important; + #{$prop}-bottom: $length !important; + } + } +} + +// Some special margin utils +.m-auto { + margin: auto !important; +} + +.mt-auto { + margin-top: auto !important; +} + +.mr-auto { + margin-right: auto !important; +} + +.mb-auto { + margin-bottom: auto !important; +} + +.ml-auto { + margin-left: auto !important; +} + +.mx-auto { + margin-right: auto !important; + margin-left: auto !important; +} + +.my-auto { + margin-top: auto !important; + margin-bottom: auto !important; +} diff --git a/src/app/fuse-fake-db/chat.ts b/src/app/fuse-fake-db/chat.ts new file mode 100644 index 00000000..62771c02 --- /dev/null +++ b/src/app/fuse-fake-db/chat.ts @@ -0,0 +1,325 @@ +export class ChatFakeDb +{ + public static contacts = [ + { + 'id' : '5725a680b3249760ea21de52', + 'name' : 'Alice Freeman', + 'avatar': 'assets/images/avatars/alice.jpg', + 'status': 'online', + 'mood' : 'I never sign anything until I pretend to read it first..' + }, + { + 'id' : '5725a680606588342058356d', + 'name' : 'Arnold', + 'avatar': 'assets/images/avatars/Arnold.jpg', + 'status': 'do-not-disturb', + 'mood' : 'Looks like Andrew Jackson\'s been tossed to the back of the bus.' + }, + { + 'id' : '5725a68009e20d0a9e9acf2a', + 'name' : 'Barrera', + 'avatar': 'assets/images/avatars/Barrera.jpg', + 'status': 'do-not-disturb', + 'mood' : 'Love is going to bed early.Marriage is going to sleep early.', + 'unread': null + }, + { + 'id' : '5725a6809fdd915739187ed5', + 'name' : 'Blair', + 'avatar': 'assets/images/avatars/Blair.jpg', + 'status': 'offline', + 'mood' : 'I would be unstoppable. If i could just get started.', + 'unread': 3 + }, + { + 'id' : '5725a68007920cf75051da64', + 'name' : 'Boyle', + 'avatar': 'assets/images/avatars/Boyle.jpg', + 'status': 'offline', + 'mood' : '\'GOOD MORNING COFFEE\'....Meet your maker!!!!' + }, + { + 'id' : '5725a68031fdbb1db2c1af47', + 'name' : 'Christy', + 'avatar': 'assets/images/avatars/Christy.jpg', + 'status': 'offline', + 'mood' : 'We always hold hands. If I let go, she shops.', + }, + { + 'id' : '5725a680bc670af746c435e2', + 'name' : 'Copeland', + 'avatar': 'assets/images/avatars/Copeland.jpg', + 'status': 'online', + 'mood' : 'I get enough exercise just pushing my luck.', + }, + { + 'id' : '5725a680e7eb988a58ddf303', + 'name' : 'Estes', + 'avatar': 'assets/images/avatars/Estes.jpg', + 'status': 'away', + 'mood' : 'What comes after the man bun hairstyle? The he-hive!', + }, + { + 'id' : '5725a680dcb077889f758961', + 'name' : 'Harper', + 'avatar': 'assets/images/avatars/Harper.jpg', + 'status': 'offline', + 'mood' : 'Always try to be modest and be proud of it!', + }, + { + 'id' : '5725a6806acf030f9341e925', + 'name' : 'Helen', + 'avatar': 'assets/images/avatars/Helen.jpg', + 'status': 'away', + 'mood' : 'Why are there stitch marks on zombies? Who\'s giving them medical attention?', + }, + { + 'id' : '5725a680ae1ae9a3c960d487', + 'name' : 'Henderson', + 'avatar': 'assets/images/avatars/Henderson.jpg', + 'status': 'offline', + 'mood' : 'I can\'t decide if people who wear pajamas in public have given up on life or are living it to the fullest.', + }, + { + 'id' : '5725a680b8d240c011dd224b', + 'name' : 'Josefina', + 'avatar': 'assets/images/avatars/Josefina.jpg', + 'status': 'online', + 'mood' : 'The fastest way to being happy is to make other people happy. You go first', + }, + { + 'id' : '5725a68034cb3968e1f79eac', + 'name' : 'Katina', + 'avatar': 'assets/images/avatars/Katina.jpg', + 'status': 'away', + 'mood' : 'If I was a rat,,, I wouldn\'t give anyone my ass.', + }, + { + 'id' : '5725a6801146cce777df2a08', + 'name' : 'Lily', + 'avatar': 'assets/images/avatars/Lily.jpg', + 'status': 'do-not-disturb', + 'mood' : 'A zip line but from the sofa to the fridge', + }, + { + 'id' : '5725a6808a178bfd034d6ecf', + 'name' : 'Mai', + 'avatar': 'assets/images/avatars/Mai.jpg', + 'status': 'away', + 'mood' : 'If a girl tells you she has a nipple ring, the only correct response is \'I don\'t believe you.\'', + }, + { + 'id' : '5725a680653c265f5c79b5a9', + 'name' : 'Nancy', + 'avatar': 'assets/images/avatars/Nancy.jpg', + 'status': 'do-not-disturb', + 'mood' : 'Prison counts as a gated community, right?', + }, + { + 'id' : '5725a680bbcec3cc32a8488a', + 'name' : 'Nora', + 'avatar': 'assets/images/avatars/Nora.jpg', + 'status': 'do-not-disturb', + 'mood' : 'I never date left handed women. Righty tighty, lefty loosey.', + }, + { + 'id' : '5725a6803d87f1b77e17b62b', + 'name' : 'Odessa', + 'avatar': 'assets/images/avatars/Odessa.jpg', + 'status': 'away', + 'mood' : 'A day without sunshine is like, night.', + }, + { + 'id' : '5725a680e87cb319bd9bd673', + 'name' : 'Reyna', + 'avatar': 'assets/images/avatars/Reyna.jpg', + 'status': 'offline', + 'mood' : 'I can\'t wait for summer in Canada...', + }, + { + 'id' : '5725a6802d10e277a0f35775', + 'name' : 'Shauna', + 'avatar': 'assets/images/avatars/Shauna.jpg', + 'status': 'online', + 'mood' : 'My take home pay doesn’t ven take me home.', + 'unread': null, + }, + { + 'id' : '5725a680aef1e5cf26dd3d1f', + 'name' : 'Shepard', + 'avatar': 'assets/images/avatars/Shepard.jpg', + 'status': 'online', + 'mood' : 'I don\'t speak Spanish, but I\'m pretty sure \'Dora\' means \'annoying\'', + }, + { + 'id' : '5725a680cd7efa56a45aea5d', + 'name' : 'Tillman', + 'avatar': 'assets/images/avatars/Tillman.jpg', + 'status': 'do-not-disturb', + 'mood' : '', + }, + { + 'id' : '5725a680fb65c91a82cb35e2', + 'name' : 'Trevino', + 'avatar': 'assets/images/avatars/Trevino.jpg', + 'status': 'away', + 'mood' : 'Apparently, a rat and a plastic tube does not count as a DIY abortion kit.', + }, + { + 'id' : '5725a68018c663044be49cbf', + 'name' : 'Tyson', + 'avatar': 'assets/images/avatars/Tyson.jpg', + 'status': 'do-not-disturb', + 'mood' : 'I\'m wondering why life keeps teaching me lessons I have no desire to learn...', + }, + { + 'id' : '5725a6809413bf8a0a5272b1', + 'name' : 'Velazquez', + 'avatar': 'assets/images/avatars/Velazquez.jpg', + 'status': 'online', + 'mood' : 'Modulation in all things.', + } + ]; + + public static chats = [ + { + 'id' : '1725a680b3249760ea21de52', + 'dialog': [ + { + 'who' : '5725a680b3249760ea21de52', + 'message': 'Quickly come to the meeting room 1B, we have a big server issue', + 'time' : '2017-03-22T08:54:28.299Z' + }, + { + 'who' : '5725a6802d10e277a0f35724', + 'message': 'I’m having breakfast right now, can’t you wait for 10 minutes?', + 'time' : '2017-03-22T08:55:28.299Z' + }, + { + 'who' : '5725a680b3249760ea21de52', + 'message': 'We are losing money! Quick!', + 'time' : '2017-03-22T09:00:28.299Z' + }, + { + 'who' : '5725a6802d10e277a0f35724', + 'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.', + 'time' : '2017-03-22T09:02:28.299Z' + }, + { + 'who' : '5725a680b3249760ea21de52', + 'message': 'You are the worst!', + 'time' : '2017-03-22T09:05:28.299Z' + }, + { + 'who' : '5725a680b3249760ea21de52', + 'message': 'We are losing money! Quick!', + 'time' : '2017-03-22T09:15:28.299Z' + }, + { + 'who' : '5725a6802d10e277a0f35724', + 'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.', + 'time' : '2017-03-22T09:20:28.299Z' + }, + { + 'who' : '5725a680b3249760ea21de52', + 'message': 'You are the worst!', + 'time' : '2017-03-22T09:22:28.299Z' + }, + { + 'who' : '5725a680b3249760ea21de52', + 'message': 'We are losing money! Quick!', + 'time' : '2017-03-22T09:25:28.299Z' + }, + { + 'who' : '5725a6802d10e277a0f35724', + 'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.', + 'time' : '2017-03-22T09:27:28.299Z' + }, + { + 'who' : '5725a680b3249760ea21de52', + 'message': 'You are the worst!', + 'time' : '2017-03-22T09:33:28.299Z' + }, + { + 'who' : '5725a680b3249760ea21de52', + 'message': 'We are losing money! Quick!', + 'time' : '2017-03-22T09:35:28.299Z' + }, + { + 'who' : '5725a6802d10e277a0f35724', + 'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.', + 'time' : '2017-03-22T09:45:28.299Z' + }, + { + 'who' : '5725a680b3249760ea21de52', + 'message': 'You are the worst!', + 'time' : '2017-03-22T10:00:28.299Z' + } + ] + }, + { + 'id' : '2725a680b8d240c011dd2243', + 'dialog': [ + { + 'who' : '5725a680b8d240c011dd224b', + 'message': 'Quickly come to the meeting room 1B, we have a big server issue', + 'time' : '2017-04-22T01:00:00.299Z' + }, + { + 'who' : '5725a6802d10e277a0f35724', + 'message': 'I’m having breakfast right now, can’t you wait for 10 minutes?', + 'time' : '2017-04-22T01:05:00.299Z' + }, + { + 'who' : '5725a680b8d240c011dd224b', + 'message': 'We are losing money! Quick!', + 'time' : '2017-04-22T01:10:00.299Z' + } + ] + }, + { + 'id' : '3725a6809413bf8a0a5272b4', + 'dialog': [ + { + 'who' : '5725a6809413bf8a0a5272b1', + 'message': 'Quickly come to the meeting room 1B, we have a big server issue', + 'time' : '2017-04-22T02:10:00.299Z' + } + ] + } + ]; + + public static user = [ + { + 'id' : '5725a6802d10e277a0f35724', + 'name' : 'John Doe', + 'avatar' : 'assets/images/avatars/profile.jpg', + 'status' : 'online', + 'mood' : 'it\'s a status....not your diary...', + 'chatList': [ + { + 'id' : '1725a680b3249760ea21de52', + 'contactId' : '5725a680b3249760ea21de52', + 'name' : 'Alice Freeman', + 'unread' : 4, + 'lastMessageTime': '2017-06-12T02:10:18.931Z' + }, + { + 'id' : '2725a680b8d240c011dd2243', + 'contactId' : '5725a680b8d240c011dd224b', + 'name' : 'Josefina', + 'unread' : null, + 'lastMessageTime': '2017-02-18T10:30:18.931Z' + }, + { + 'id' : '3725a6809413bf8a0a5272b4', + 'contactId' : '5725a6809413bf8a0a5272b1', + 'name' : 'Velazquez', + 'unread' : 2, + 'lastMessageTime': '2017-03-18T12:30:18.931Z' + } + ] + } + ]; + +} diff --git a/src/app/fuse-fake-db/fuse-fake-db.service.ts b/src/app/fuse-fake-db/fuse-fake-db.service.ts index 143b82d6..dc3b15bf 100644 --- a/src/app/fuse-fake-db/fuse-fake-db.service.ts +++ b/src/app/fuse-fake-db/fuse-fake-db.service.ts @@ -1,5 +1,6 @@ import {InMemoryDbService} from 'angular-in-memory-web-api'; import {MailFakeDb} from './mail'; +import {ChatFakeDb} from './chat'; export class FuseFakeDbService implements InMemoryDbService { @@ -7,8 +8,11 @@ export class FuseFakeDbService implements InMemoryDbService { return { 'mail-mails' : MailFakeDb.mails, - 'mail-folders': MailFakeDb.folders, - 'mail-labels' : MailFakeDb.labels + 'mail-folders' : MailFakeDb.folders, + 'mail-labels' : MailFakeDb.labels, + 'chat-contacts': ChatFakeDb.contacts, + 'chat-chats' : ChatFakeDb.chats, + 'chat-user' : ChatFakeDb.user, }; } diff --git a/src/app/main/apps/chat/chat-start/chat-start.component.html b/src/app/main/apps/chat/chat-start/chat-start.component.html new file mode 100644 index 00000000..d07bb51d --- /dev/null +++ b/src/app/main/apps/chat/chat-start/chat-start.component.html @@ -0,0 +1,17 @@ +
+ +
+ + chat + +
+ + Chat App + + Select contact to start the chat!.. + + + +
diff --git a/src/app/main/apps/chat/chat-start/chat-start.component.scss b/src/app/main/apps/chat/chat-start/chat-start.component.scss new file mode 100644 index 00000000..b0e1afde --- /dev/null +++ b/src/app/main/apps/chat/chat-start/chat-start.component.scss @@ -0,0 +1,30 @@ +@import "src/app/core/scss/fuse"; + +:host { + display: flex; + flex: 1; + height: 100%; + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 20%, rgba(255, 255, 255, 0.8)); + + .big-circle { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 20%, rgba(255, 255, 255, 0.8)); + border-radius: 50%; + width: 300px; + height: 300px; + line-height: 300px; + text-align: center; + + md-icon{ + color: mat-color($accent); + } + } + + .app-title { + font-weight: 500; + font-size: 32px; + } + + .secondary-text { + font-size: 16px; + } +} diff --git a/src/app/main/apps/chat/chat-start/chat-start.component.ts b/src/app/main/apps/chat/chat-start/chat-start.component.ts new file mode 100644 index 00000000..ab7a6e51 --- /dev/null +++ b/src/app/main/apps/chat/chat-start/chat-start.component.ts @@ -0,0 +1,19 @@ +import {Component, OnInit} from '@angular/core'; + +@Component({ + selector : 'fuse-chat-start', + templateUrl: './chat-start.component.html', + styleUrls : ['./chat-start.component.scss'] +}) +export class ChatStartComponent implements OnInit +{ + + constructor() + { + } + + ngOnInit() + { + } + +} diff --git a/src/app/main/apps/chat/chat-view/chat-view.component.html b/src/app/main/apps/chat/chat-view/chat-view.component.html new file mode 100644 index 00000000..7168346f --- /dev/null +++ b/src/app/main/apps/chat/chat-view/chat-view.component.html @@ -0,0 +1,115 @@ + +
+ + + + +
+ +
+ + +
+ chat +
+ + + +
+ +
+ + {{contact.name}} + + + +
+ +
+ {{contact.name}} +
+ +
+ +
+ +
+ + + + + +
+ +
+
+ + + +
+ + +
+ + +
+ + {{contact.name}} + + + +
+
{{message.message}}
+
{{message.time | date:'medium'}}
+
+ +
+ + +
+ + +
+ + + + + +
+ diff --git a/src/app/main/apps/chat/chat-view/chat-view.component.scss b/src/app/main/apps/chat/chat-view/chat-view.component.scss new file mode 100644 index 00000000..90a8a613 --- /dev/null +++ b/src/app/main/apps/chat/chat-view/chat-view.component.scss @@ -0,0 +1,135 @@ +:host { + display: flex; + flex: 1; + height: 100%; + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 20%, rgba(255, 255, 255, 0.8)); + overflow: hidden; + + .chat { + height: 100%; + + .chat-toolbar { + min-height: 64px; + background-color: #F3F4F5; + color: rgba(0, 0, 0, 0.87); + border-bottom: 1px solid rgba(0, 0, 0, .08); + + .responsive-chats-button { + padding: 0; + } + + .chat-contact { + cursor: pointer; + + .avatar { + margin-right: 16px; + } + + .chat-contact-name { + + } + } + } + + #chat-content { + background: transparent; + + .message-row { + padding: 16px; + + .bubble { + position: relative; + padding: 6px 7px 8px 9px; + background-color: #FFF; + box-shadow: 0 1px .5px rgba(0, 0, 0, .13); + border-radius: 6px; + + &:before { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAMAAADp2asXAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADGUExURQAAAP////b29vn5+f///wAAAP///wAAAAAAAP///9ra2v////j4+PHx8fv7++Hh4fHx8f////////////////39/QAAAP////////z8/P////39/f39/fz8/P////////////z8/P////////////z8/P////////////v7+/Hx8f///9bW1vz8/K2trf////39/f39/WJiYgAAAExMTFtbWwAAAN3d3cjIyPr6+vX19QAAAO7u7vz8/NTU1Ofn5zMzM////zGPlXsAAABBdFJOUwAcm/kREh4CCDWL1SneR6TfAQffhMYK/A5nRrLWfRc5DW2ih5f+19Kn+9v4g/1LCJuXHwQUKgahcXS6DNnlDMMKKzPoTgAAAKBJREFUKM+V08USwmAQA+C/0NIWd3d3d8/7vxTMcIPkQK7f7CG7s8bQAOY/SCuwFYQU1P+eiCqIK2gpWCmoCrAgoKQgJ8CHgIqAMjg0MxxSQ3DogEMWFBZtUPAHYGB1CyDQWE6AH7BrfXzlAxGAQhECTGAmwN1Okz0Gb/LW4fEItIfrOfNELMh3tck7u+PhcT2zQ7l77/K8iY8yJwV3BeYFqpc/uSyPGdAAAAAASUVORK5CYII=); + content: ''; + position: absolute; + left: -11px; + bottom: 3px; + width: 12px; + height: 19px; + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: contain; + } + + .message { + white-space: pre-wrap; + } + + .time { + font-size: 11px; + margin-top: 8px; + text-align: right; + } + } + + &.contact { + + .avatar { + margin: 0 16px 0 0; + } + } + + &.user { + align-items: flex-end; + + .avatar { + order: 2; + margin: 0 0 0 16px; + } + + .bubble { + margin-left: auto; + background-color: #E8F5E9; + border: 1px solid #DFEBE0; + order: 1; + &:before { + right: -11px; + left: auto; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAMAAADp2asXAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD2UExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRsXAAAANzwzNPmxNrtyau5oIWRedDkwNntyczgwdfpyJ+/n97wzsLWtNjsytvwzczfvtPmxau6nNjqxtrtyio1KtzwzNjryAAAANzwzgAAANzwzK7Aor/Us9Lnw8vevAAAAMzevtbpxrvMrX+IdwAAAEROOi45Lr3MrZGjf9LoxX+MctnqydLkwhgYGMzfv9vuyQAAANzwzNvuy9zxy7vMu7XGqNvtzKKykwAAANruzKq6nLnMriQkGMXXuL3PsNjsySgzKAAAANLkw83fvd3vy9z4xtzwzRpFmIEAAABQdFJOUwAXChEGBAMBAgwhDvJ7k0YqMc0Zmwj6apf2kjU0+dkw/swh/CP9j2Wr2gndvaYeBRoxQg6gUPt/FaHJGdTj9A9k7XQLeE6iFcN12xkSt9r4NKizowAAAMFJREFUKM+V0sdywlAMBVDbMX7PQCihQ+iQ0HsJvfem/P/PwBIzugu0PXNnNNJVyPmhsIPhhoB2COwIGuLdhAcl3AhCBoBoHUC6BCBbA0C/EkBFB5D/FjxQwQYg1RI8UKINgDoSAPUlAPqUAMgfAEBfXsEDBV0+Hogi4Zhg4THj9YwHoqEBYOrgYTI3GVgMNn8r+Qq94k9yZNosW/3Hy9VuTjWfHkOX6367bGZUU7de66ieHZrO1OGg8Z1WTgYAFLgD5S1PCkzo1B0AAAAASUVORK5CYII=); + } + } + } + } + } + + .chat-footer { + min-height: 64px; + max-height: 96px; + background-color: #F3F4F5; + color: rgba(0, 0, 0, 0.87); + border-top: 1px solid rgba(0, 0, 0, .08); + padding: 8px 8px 8px 16px; + + .reply-form { + + md-input-container { + margin: 0; + padding-right: 16px; + + textarea { + overflow: auto; + max-height: 80px; + transition: height 200ms ease; + &.grow { + height: 80px; + } + } + + .md-errors-spacer { + display: none; + } + } + + .md-button { + margin: 0; + } + } + } + } +} diff --git a/src/app/main/apps/chat/chat-view/chat-view.component.ts b/src/app/main/apps/chat/chat-view/chat-view.component.ts new file mode 100644 index 00000000..24bcf63d --- /dev/null +++ b/src/app/main/apps/chat/chat-view/chat-view.component.ts @@ -0,0 +1,101 @@ +import { AfterViewInit, Component, OnInit, ViewChild, ViewChildren } from '@angular/core'; +import { ChatService } from '../chat.service'; +import { NgForm } from '@angular/forms'; +import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar'; + +@Component({ + selector : 'fuse-chat-view', + templateUrl: './chat-view.component.html', + styleUrls : ['./chat-view.component.scss'] +}) +export class ChatViewComponent implements OnInit, AfterViewInit +{ + user: any; + chat: any; + dialog: any; + contact: any; + replyInput: any; + selectedChat: any; + @ViewChild(PerfectScrollbarDirective) directiveScroll: PerfectScrollbarDirective; + @ViewChildren('replyInput') replyInputField; + @ViewChild('replyForm') replyForm: NgForm; + + constructor(private chatService: ChatService) + { + } + + ngOnInit() + { + this.user = this.chatService.user; + this.chatService.onChatSelected + .subscribe(chatData => { + if ( chatData ) + { + this.selectedChat = chatData; + this.contact = chatData.contact; + this.dialog = chatData.dialog; + this.readyToReply(); + } + }); + } + + ngAfterViewInit() + { + this.replyInput = this.replyInputField.first.nativeElement; + this.readyToReply(); + } + + selectContact() + { + this.chatService.selectContact(this.contact); + } + + readyToReply() + { + setTimeout(() => { + this.replyForm.reset(); + this.focusReplyInput(); + this.scrollToBottom(); + }); + + } + + focusReplyInput() + { + setTimeout(() => { + this.replyInput.focus(); + }); + } + + scrollToBottom(speed?: number) + { + speed = speed || 400; + if ( this.directiveScroll ) + { + setTimeout(() => { + this.directiveScroll.scrollToBottom(0, speed); + }); + } + + } + + + reply(event) + { + // Message + const message = { + who : this.user.id, + message: this.replyForm.form.value.message, + time : new Date().toISOString() + }; + + // Add the message to the chat + this.dialog.push(message); + + // Update the server + this.chatService.updateDialog(this.selectedChat.chatId, this.dialog).then(response => { + this.readyToReply(); + }); + + } +} diff --git a/src/app/main/apps/chat/chat.component.html b/src/app/main/apps/chat/chat.component.html index 3a6e1396..145788aa 100644 --- a/src/app/main/apps/chat/chat.component.html +++ b/src/app/main/apps/chat/chat.component.html @@ -1,3 +1,43 @@ -

- chat works! -

+
+ + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
diff --git a/src/app/main/apps/chat/chat.component.scss b/src/app/main/apps/chat/chat.component.scss index e69de29b..9f6a3c33 100644 --- a/src/app/main/apps/chat/chat.component.scss +++ b/src/app/main/apps/chat/chat.component.scss @@ -0,0 +1,31 @@ +:host { + height: 100% !important; + + .center { + padding: 32px 32px 0 32px; + max-width: 1400px; + height: 100%; + margin: 0 auto; + + .content-card { + position: relative; + background: url('/assets/images/patterns/rain-grey.png') repeat; + height: 100%; + + .mat-sidenav-container { + width: 100%; + height: 100%; + background: transparent; + + md-sidenav { + display: flex; + flex-direction: column; + width: 400px; + max-width: 90%; + box-shadow: 0 0 1px rgba(0, 0, 0, .37); + overflow: hidden; + } + } + } + } +} diff --git a/src/app/main/apps/chat/chat.component.ts b/src/app/main/apps/chat/chat.component.ts index dae90173..85c78ee9 100644 --- a/src/app/main/apps/chat/chat.component.ts +++ b/src/app/main/apps/chat/chat.component.ts @@ -1,15 +1,25 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { ChatService } from './chat.service'; @Component({ - selector: 'fuse-chat', - templateUrl: './chat.component.html', - styleUrls: ['./chat.component.scss'] + selector : 'fuse-chat', + templateUrl: './chat.component.html', + styleUrls : ['./chat.component.scss'] }) -export class ChatComponent implements OnInit { +export class ChatComponent implements OnInit +{ + selectedChat: any; - constructor() { } + constructor(private chatService: ChatService) + { + } - ngOnInit() { - } + ngOnInit() + { + this.chatService.onChatSelected + .subscribe(chatData => { + this.selectedChat = chatData; + }); + } } diff --git a/src/app/main/apps/chat/chat.module.ts b/src/app/main/apps/chat/chat.module.ts index 4f5050d8..284b9406 100644 --- a/src/app/main/apps/chat/chat.module.ts +++ b/src/app/main/apps/chat/chat.module.ts @@ -2,10 +2,21 @@ import {NgModule} from '@angular/core'; import {SharedModule} from '../../../core/modules/shared.module'; import {RouterModule, Routes} from '@angular/router'; import {ChatComponent} from './chat.component'; +import {ChatService} from './chat.service'; +import { ChatViewComponent } from './chat-view/chat-view.component'; +import { ChatStartComponent } from './chat-start/chat-start.component'; +import {ChatsSidenavComponent} from './sidenavs/left/chats/chats.component'; +import { UserSidenavComponent } from './sidenavs/left/user/user.component'; +import { LeftSidenavComponent } from './sidenavs/left/left.component'; +import { RightSidenavComponent } from './sidenavs/right/right.component'; +import { ContactSidenavComponent } from './sidenavs/right/contact/contact.component'; const routes: Routes = [ { - path: 'apps/chat', component: ChatComponent, children: [] + path : 'apps/chat', component: ChatComponent, children: [], + resolve: { + chat: ChatService + } } ]; @@ -15,7 +26,17 @@ const routes: Routes = [ RouterModule.forChild(routes) ], declarations: [ - ChatComponent + ChatComponent, + ChatViewComponent, + ChatStartComponent, + ChatsSidenavComponent, + UserSidenavComponent, + LeftSidenavComponent, + RightSidenavComponent, + ContactSidenavComponent + ], + providers : [ + ChatService ] }) export class ChatModule diff --git a/src/app/main/apps/chat/chat.service.ts b/src/app/main/apps/chat/chat.service.ts new file mode 100644 index 00000000..484603d8 --- /dev/null +++ b/src/app/main/apps/chat/chat.service.ts @@ -0,0 +1,259 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; +import { Http } from '@angular/http'; +import { Subject } from 'rxjs/Subject'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +@Injectable() +export class ChatService implements Resolve +{ + contacts: any[]; + chats: any[]; + user: any; + onChatSelected = new BehaviorSubject(null); + onContactSelected = new BehaviorSubject(null); + onChatsUpdated = new Subject(); + onUserUpdated = new Subject(); + onLeftSidenavViewChanged = new Subject(); + onRightSidenavViewChanged = new Subject(); + + constructor(private http: Http) + { + } + + /** + * Get chat + * @param contactId + * @returns {Promise} + */ + getChat(contactId) + { + const chatItem = this.user.chatList.find((item) => { + return item.contactId === contactId; + }); + + /** + * Create new chat, if it's not created yet. + */ + if ( !chatItem ) + { + this.createNewChat(contactId).then((newChats) => { + this.getChat(contactId); + }); + return; + } + + return new Promise((resolve, reject) => { + this.http.get('api/chat-chats/' + chatItem.id) + .subscribe(response => { + const chat = response.json().data; + + const chatContact = this.contacts.find((contact) => { + return contact.id === contactId; + }); + + const chatData = { + chatId : chat.id, + dialog : chat.dialog, + contact: chatContact + }; + + this.onChatSelected.next({...chatData}); + + }, reject); + + }); + + } + + /** + * Create New Chat + * @param contactId + * @returns {Promise} + */ + createNewChat(contactId) + { + return new Promise((resolve, reject) => { + + const contact = this.contacts.find((item) => { + return item.id === contactId; + }); + + const chatId = this.guidGenerator(); + + const chat = { + id : chatId, + dialog: [] + }; + + const chatListItem = { + 'id' : chatId, + 'contactId' : contactId, + 'unread' : null, + 'lastMessageTime': '2017-02-18T10:30:18.931Z' + }; + + /** + * Add new chat list item to the user's chat list + */ + this.user.chatList.push(chatListItem); + + /** + * Post the created chat + */ + this.http.post('api/chat-chats', {...chat}) + .subscribe(response => { + + /** + * Post the new the user data + */ + this.http.post('api/chat-user/' + this.user.id, this.user) + .subscribe(addedlistItem => { + + /** + * Update the user data from server + */ + this.getUser().then(updatedUser => { + this.onUserUpdated.next(updatedUser); + resolve(updatedUser); + }); + }); + }, reject); + }); + } + + /** + * Select Contact + * @param contact + */ + selectContact(contact) + { + this.onContactSelected.next(contact); + } + + /** + * Set user status + * @param status + */ + setUserStatus(status) + { + this.user.status = status; + } + + /** + * Update user data + * @param userData + */ + updateUserData(userData) + { + this.http.post('api/chat-user/' + this.user.id, userData) + .subscribe(response => { + this.user = userData; + } + ); + } + + /** + * Update the chat dialog + * @param chatId + * @param dialog + * @returns {Promise} + */ + updateDialog(chatId, dialog): Promise + { + return new Promise((resolve, reject) => { + + const newData = { + id : chatId, + dialog: dialog + }; + + this.http.post('api/chat-chats/' + chatId, newData) + .subscribe(updatedChat => { + resolve(updatedChat); + }, reject); + }); + } + + /** + * The Mail App Main Resolver + * @param {ActivatedRouteSnapshot} route + * @param {RouterStateSnapshot} state + * @returns {Observable | Promise | any} + */ + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | 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} + */ + getContacts(): Promise + { + return new Promise((resolve, reject) => { + this.http.get('api/chat-contacts') + .subscribe(response => { + resolve(response.json().data); + }, reject); + }); + } + + /** + * Get Chats + * @returns {Promise} + */ + getChats(): Promise + { + return new Promise((resolve, reject) => { + this.http.get('api/chat-chats') + .subscribe(response => { + resolve(response.json().data); + }, reject); + }); + } + + /** + * Get User + * @returns {Promise} + */ + getUser(): Promise + { + return new Promise((resolve, reject) => { + this.http.get('api/chat-user') + .subscribe(response => { + resolve(response.json().data[0]); + }, reject); + }); + } + + /** + * Random ID Generator + * @returns {string} + */ + guidGenerator() + { + function S4() + { + return (((1 + Math.random()) * 0x10000) || 0).toString(16).substring(1); + } + + return (S4() + S4()); + } +} diff --git a/src/app/main/apps/chat/sidenavs/left/chats/chats.component.html b/src/app/main/apps/chat/sidenavs/left/chats/chats.component.html new file mode 100644 index 00000000..04cb1cc8 --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/left/chats/chats.component.html @@ -0,0 +1,183 @@ + +
+ + + + +
+ + +
+ + + {{user.name}} + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + +
+
+ + + + + + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+ + +
+ + +
+ +
+ Chats +
+ + + +
+ + + +
+ +
+ Contacts +
+ + +
+ + + +
+ No results.. +
+ + +
+ + +
+ diff --git a/src/app/main/apps/chat/sidenavs/left/chats/chats.component.scss b/src/app/main/apps/chat/sidenavs/left/chats/chats.component.scss new file mode 100644 index 00000000..c2b36587 --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/left/chats/chats.component.scss @@ -0,0 +1,110 @@ +@import "src/app/core/scss/fuse"; + +:host { + display: flex; + flex: 1; + flex-direction: column; + + .sidenav-header { + + md-toolbar { + border-bottom: 1px solid rgba(0, 0, 0, .08); + + .avatar-wrapper { + + .avatar, .status { + cursor: pointer; + } + } + + .search { + height: 36px; + line-height: 36px; + padding: 8px; + background: #FFFFFF; + font-size: 13px; + @include mat-elevation(1); + + .icon { + margin: 0; + color: rgba(0, 0, 0, 0.54); + } + + input { + padding-left: 12px; + height: 36px; + color: rgba(0, 0, 0, 0.54); + border: none; + } + } + } + } + + .sidenav-content { + .contact-list, .chat-list { + + .mat-subheader { + padding-left: 16px; + font-size: 20px; + font-weight: 300; + height: 88px; + line-height: 88px; + color: mat-color($accent); + } + + .contact { + white-space: normal; + text-align: left; + letter-spacing: .010em; + min-height: 88px; + border-bottom: 1px solid rgba(0, 0, 0, 0.12); + padding: 16px; + font-weight: 400; + + .avatar-wrapper { + + .avatar { + margin-right: 16px; + } + } + + .contact-name { + font-size: 16px; + white-space: nowrap; + text-overflow: ellipsis; + } + + .contact-last-message { + line-height: 1.6em; + margin: 0; + font-weight: 500; + color: rgba(0, 0, 0, 0.54); + } + + .contact-mood { + + } + + .unread-message-count { + border-radius: 50%; + text-align: center; + width: 24px; + height: 24px; + line-height: 24px; + background-color: mat-color($accent); + color: map-get($accent, default-contrast); + } + } + } + + .no-results-message { + position: absolute; + width: 100%; + height: 88px; + padding: 16px; + background: #FFFFFF; + font-size: 15px; + font-weight: 400; + } + } +} diff --git a/src/app/main/apps/chat/sidenavs/left/chats/chats.component.ts b/src/app/main/apps/chat/sidenavs/left/chats/chats.component.ts new file mode 100644 index 00000000..76ddc7c1 --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/left/chats/chats.component.ts @@ -0,0 +1,66 @@ +import { Component, OnInit } from '@angular/core'; +import { ChatService } from '../../../chat.service'; +import { FuseMdSidenavHelperService } from '../../../../../../core/directives/md-sidenav-helper/md-sidenav-helper.service'; +import { ObservableMedia } from '@angular/flex-layout'; + +@Component({ + selector : 'fuse-chat-chats-sidenav', + templateUrl: './chats.component.html', + styleUrls : ['./chats.component.scss'] +}) +export class ChatsSidenavComponent implements OnInit +{ + user: any; + chats: any[]; + contacts: any[]; + chatSearch: any; + + constructor(private chatService: ChatService, + private fuseMdSidenavService: FuseMdSidenavHelperService, + public media: ObservableMedia) + { + this.chatSearch = { + name: '' + }; + } + + ngOnInit() + { + this.user = this.chatService.user; + this.chats = this.chatService.chats; + this.contacts = this.chatService.contacts; + + this.chatService.onChatsUpdated.subscribe(updatedChats => { + this.chats = updatedChats; + }); + + this.chatService.onUserUpdated.subscribe(updatedUser => { + this.user = updatedUser; + }); + } + + getChat(contact) + { + this.chatService.getChat(contact); + + if ( !this.media.isActive('gt-md') ) + { + this.fuseMdSidenavService.getSidenav('chat-left-sidenav').toggle(); + } + } + + setUserStatus(status) + { + this.chatService.setUserStatus(status); + } + + changeLeftSidenavView(view) + { + this.chatService.onLeftSidenavViewChanged.next(view); + } + + logout() + { + console.info('logout triggered'); + } +} diff --git a/src/app/main/apps/chat/sidenavs/left/left.component.html b/src/app/main/apps/chat/sidenavs/left/left.component.html new file mode 100644 index 00000000..43df8660 --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/left/left.component.html @@ -0,0 +1,11 @@ +
+ + + + + +
diff --git a/src/app/main/apps/chat/sidenavs/left/left.component.scss b/src/app/main/apps/chat/sidenavs/left/left.component.scss new file mode 100644 index 00000000..3661f0ce --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/left/left.component.scss @@ -0,0 +1,20 @@ +:host { + display: flex; + flex-direction: column; + height: 100%; + + .views { + display: flex; + flex-direction: column; + height: 100%; + + .view { + position: absolute; + height: 100%; + bottom: 0; + left: 0; + right: 0; + top: 0; + } + } +} diff --git a/src/app/main/apps/chat/sidenavs/left/left.component.ts b/src/app/main/apps/chat/sidenavs/left/left.component.ts new file mode 100644 index 00000000..0b16799f --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/left/left.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit } from '@angular/core'; +import { Animations } from '../../../../../core/animations'; +import { ChatService } from '../../chat.service'; + +@Component({ + selector : 'fuse-chat-left-sidenav', + templateUrl: './left.component.html', + styleUrls : ['./left.component.scss'], + animations : [Animations.slideInLeft, Animations.slideInRight] +}) +export class LeftSidenavComponent implements OnInit +{ + view: string; + + constructor(private chatService: ChatService) + { + this.view = 'chats'; + } + + ngOnInit() + { + this.chatService.onLeftSidenavViewChanged.subscribe(view => { + this.view = view; + }); + } + +} diff --git a/src/app/main/apps/chat/sidenavs/left/user/user.component.html b/src/app/main/apps/chat/sidenavs/left/user/user.component.html new file mode 100644 index 00000000..00fa8bbb --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/left/user/user.component.html @@ -0,0 +1,78 @@ + +
+ + + + + +
+ + + +
+ + + + + + {{user.name}} + +
{{user.name}}
+ +
+ + +
+ +
+ + +
+ + + + +
+ + + + + + + + +
+ + Online +
+
+ + +
+ + Away +
+
+ + +
+ + Do not disturb +
+
+ + +
+ + Offline +
+
+
+ +
+
+ +
diff --git a/src/app/main/apps/chat/sidenavs/left/user/user.component.scss b/src/app/main/apps/chat/sidenavs/left/user/user.component.scss new file mode 100644 index 00000000..9a844a8f --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/left/user/user.component.scss @@ -0,0 +1,21 @@ +@import "src/app/core/scss/fuse"; + +:host { + display: flex; + flex: 1; + flex-direction: column; + + md-toolbar { + background-color: mat-color($accent); + color: map-get($accent, default-contrast); + + .toolbar-bottom { + height: 240px; + } + + } + + .sidenav-content{ + background: whitesmoke; + } +} diff --git a/src/app/main/apps/chat/sidenavs/left/user/user.component.ts b/src/app/main/apps/chat/sidenavs/left/user/user.component.ts new file mode 100644 index 00000000..9f4ce7bc --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/left/user/user.component.ts @@ -0,0 +1,47 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { ChatService } from '../../../chat.service'; +import { FormControl, FormGroup } from '@angular/forms'; +import 'rxjs/Rx'; + +@Component({ + selector : 'fuse-chat-user-sidenav', + templateUrl: './user.component.html', + styleUrls : ['./user.component.scss'] +}) +export class UserSidenavComponent implements OnInit, OnDestroy +{ + user: any; + onFormChange: any; + userForm: FormGroup; + + constructor(private chatService: ChatService) + { + this.user = this.chatService.user; + this.userForm = new FormGroup({ + mood : new FormControl(this.user.mood), + status: new FormControl(this.user.status) + }); + } + + ngOnInit() + { + this.onFormChange = this.userForm.valueChanges + .debounceTime(500) + .distinctUntilChanged() + .subscribe(data => { + this.user.mood = data.mood; + this.user.status = data.status; + this.chatService.updateUserData(this.user); + }); + } + + changeLeftSidenavView(view) + { + this.chatService.onLeftSidenavViewChanged.next(view); + } + + ngOnDestroy() + { + this.onFormChange.unsubscribe(); + } +} diff --git a/src/app/main/apps/chat/sidenavs/right/contact/contact.component.html b/src/app/main/apps/chat/sidenavs/right/contact/contact.component.html new file mode 100644 index 00000000..2677e347 --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/right/contact/contact.component.html @@ -0,0 +1,47 @@ + +
+ + + + + +
+ +
Contact Info
+ + + +
+ + + + + + {{contact.name}} + +
{{contact.name}}
+ +
+ + +
+ +
+ + +
+ + + + + + + + + + +
diff --git a/src/app/main/apps/chat/sidenavs/right/contact/contact.component.scss b/src/app/main/apps/chat/sidenavs/right/contact/contact.component.scss new file mode 100644 index 00000000..9a844a8f --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/right/contact/contact.component.scss @@ -0,0 +1,21 @@ +@import "src/app/core/scss/fuse"; + +:host { + display: flex; + flex: 1; + flex-direction: column; + + md-toolbar { + background-color: mat-color($accent); + color: map-get($accent, default-contrast); + + .toolbar-bottom { + height: 240px; + } + + } + + .sidenav-content{ + background: whitesmoke; + } +} diff --git a/src/app/main/apps/chat/sidenavs/right/contact/contact.component.ts b/src/app/main/apps/chat/sidenavs/right/contact/contact.component.ts new file mode 100644 index 00000000..70a71789 --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/right/contact/contact.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit } from '@angular/core'; +import { ChatService } from '../../../chat.service'; + +@Component({ + selector : 'fuse-chat-contact-sidenav', + templateUrl: './contact.component.html', + styleUrls : ['./contact.component.scss'] +}) +export class ContactSidenavComponent implements OnInit +{ + contact: any; + + constructor(private chatService: ChatService) + { + + } + + ngOnInit() + { + this.chatService.onContactSelected.subscribe(contact => { + this.contact = contact; + }); + } + +} diff --git a/src/app/main/apps/chat/sidenavs/right/right.component.html b/src/app/main/apps/chat/sidenavs/right/right.component.html new file mode 100644 index 00000000..a193e1f3 --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/right/right.component.html @@ -0,0 +1,8 @@ +
+ + + + +
diff --git a/src/app/main/apps/chat/sidenavs/right/right.component.scss b/src/app/main/apps/chat/sidenavs/right/right.component.scss new file mode 100644 index 00000000..3661f0ce --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/right/right.component.scss @@ -0,0 +1,20 @@ +:host { + display: flex; + flex-direction: column; + height: 100%; + + .views { + display: flex; + flex-direction: column; + height: 100%; + + .view { + position: absolute; + height: 100%; + bottom: 0; + left: 0; + right: 0; + top: 0; + } + } +} diff --git a/src/app/main/apps/chat/sidenavs/right/right.component.ts b/src/app/main/apps/chat/sidenavs/right/right.component.ts new file mode 100644 index 00000000..3006f034 --- /dev/null +++ b/src/app/main/apps/chat/sidenavs/right/right.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit } from '@angular/core'; +import { Animations } from '../../../../../core/animations'; +import { ChatService } from '../../chat.service'; + +@Component({ + selector : 'fuse-chat-right-sidenav', + templateUrl: './right.component.html', + styleUrls : ['./right.component.scss'], + animations : [Animations.slideInLeft, Animations.slideInRight] +}) +export class RightSidenavComponent implements OnInit +{ + view: string; + + constructor(private chatService: ChatService) + { + this.view = 'contact'; + } + + ngOnInit() + { + this.chatService.onRightSidenavViewChanged.subscribe(view => { + this.view = view; + }); + } + +} diff --git a/tslint.json b/tslint.json index 62ab1fea..576a0287 100644 --- a/tslint.json +++ b/tslint.json @@ -26,7 +26,7 @@ "label-position": true, "max-line-length": [ true, - 120 + 180 ], "member-access": false, "member-ordering": [ From f73564c464c2d1c4e4aa812f0a2eaf553de7de52 Mon Sep 17 00:00:00 2001 From: mustafahlvc Date: Sun, 23 Jul 2017 10:43:30 +0300 Subject: [PATCH 2/5] Unnecessary searchpipe removed.. --- src/app/core/pipes/pipes.module.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/app/core/pipes/pipes.module.ts b/src/app/core/pipes/pipes.module.ts index 0a858c00..ebcb5090 100644 --- a/src/app/core/pipes/pipes.module.ts +++ b/src/app/core/pipes/pipes.module.ts @@ -3,7 +3,6 @@ import { NgModule } from '@angular/core'; import { KeysPipe } from './keys.pipe'; import { GetByIdPipe } from './getById.pipe'; import { HtmlToPlaintextPipe } from './htmlToPlaintext.pipe'; -import { SearchPipe } from './search.pipe'; import { FilterPipe } from './filter.pipe'; @NgModule({ @@ -11,7 +10,6 @@ import { FilterPipe } from './filter.pipe'; KeysPipe, GetByIdPipe, HtmlToPlaintextPipe, - SearchPipe, FilterPipe ], @@ -20,7 +18,6 @@ import { FilterPipe } from './filter.pipe'; KeysPipe, GetByIdPipe, HtmlToPlaintextPipe, - SearchPipe, FilterPipe ] }) From 31df82fe0924113a1f2a1f7f71ff2dfd0e7fa23a Mon Sep 17 00:00:00 2001 From: mustafahlvc Date: Sun, 23 Jul 2017 18:20:48 +0300 Subject: [PATCH 3/5] (Calendar App) Started to build, run npm install.. --- package-lock.json | 37 ++++ package.json | 1 + src/app/app.module.ts | 6 +- src/app/core/modules/shared.module.ts | 1 + .../apps/calendar/calendar.component.html | 117 +++++++++++ .../apps/calendar/calendar.component.scss | 126 ++++++++++++ .../main/apps/calendar/calendar.component.ts | 188 ++++++++++++++++++ src/app/main/apps/calendar/calendar.module.ts | 35 ++++ .../main/apps/calendar/calendar.service.ts | 24 +++ .../event-dialog/event-dialog.component.html | 20 ++ .../event-dialog/event-dialog.component.scss | 0 .../event-dialog/event-dialog.component.ts | 22 ++ src/app/main/apps/chat/chat.module.ts | 2 +- 13 files changed, 576 insertions(+), 3 deletions(-) create mode 100644 src/app/main/apps/calendar/calendar.component.html create mode 100644 src/app/main/apps/calendar/calendar.component.scss create mode 100644 src/app/main/apps/calendar/calendar.component.ts create mode 100644 src/app/main/apps/calendar/calendar.module.ts create mode 100644 src/app/main/apps/calendar/calendar.service.ts create mode 100644 src/app/main/apps/calendar/event-dialog/event-dialog.component.html create mode 100644 src/app/main/apps/calendar/event-dialog/event-dialog.component.scss create mode 100644 src/app/main/apps/calendar/event-dialog/event-dialog.component.ts diff --git a/package-lock.json b/package-lock.json index 1c0e7771..2aef8461 100644 --- a/package-lock.json +++ b/package-lock.json @@ -562,11 +562,33 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, + "angular-calendar": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/angular-calendar/-/angular-calendar-0.19.0.tgz", + "integrity": "sha512-RbLciS+OBl9ITgPKVUOjMUXe7sTP5KHPZtCB7Rru3ebUXh8WehdZvELC3Wxz0euS8hqBP2o3ueSqXcAy1pz2bw==", + "requires": { + "angular-draggable-droppable": "1.0.1", + "angular-resizable-element": "1.2.0", + "calendar-utils": "0.0.56", + "date-fns": "1.28.5", + "positioning": "1.3.0" + } + }, + "angular-draggable-droppable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/angular-draggable-droppable/-/angular-draggable-droppable-1.0.1.tgz", + "integrity": "sha1-etcMMQmUsPmA9A04Lc5ZlG/jDc8=" + }, "angular-in-memory-web-api": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.3.2.tgz", "integrity": "sha1-iDbZ4lNNN7co88taHK9v4ef7vs0=" }, + "angular-resizable-element": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/angular-resizable-element/-/angular-resizable-element-1.2.0.tgz", + "integrity": "sha512-i5xCl4n2VMgGK4gY6Jtho0K5aazbsqNw1bmPYpI9RwlKK+dIOcsMRuMl1JPWzrznHsm4qEsfYg+9KLkYsYy+/g==" + }, "ansi-escapes": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-2.0.0.tgz", @@ -1217,6 +1239,11 @@ "integrity": "sha1-TJQj6i0lLCcMQbK97+/5u2tiwGo=", "dev": true }, + "calendar-utils": { + "version": "0.0.56", + "resolved": "https://registry.npmjs.org/calendar-utils/-/calendar-utils-0.0.56.tgz", + "integrity": "sha512-IvvzvIGmtDdVjSrnIOd5dDJ3ATWLztqdgLOTeIB2IJonN60LMa2R71KDVpdF8w9sKm3drRV4kfgIbocYJAw6+Q==" + }, "callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", @@ -1950,6 +1977,11 @@ } } }, + "date-fns": { + "version": "1.28.5", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.28.5.tgz", + "integrity": "sha1-JXz8RdMi30XvVlhmWWfuhBzXP68=" + }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", @@ -6462,6 +6494,11 @@ } } }, + "positioning": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/positioning/-/positioning-1.3.0.tgz", + "integrity": "sha512-B0BHlhLFsLPV8EWVv792caQCg4QNxuCeZUVXw/DP1jRj4WOF74KmTAg+7t3dDfrFXDT22qBS2vcryQmZYSM7jg==" + }, "postcss": { "version": "5.2.17", "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", diff --git a/package.json b/package.json index 9eb2302c..5ebed461 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@angular/platform-browser": "^4.3.0", "@angular/platform-browser-dynamic": "^4.3.0", "@angular/router": "^4.3.0", + "angular-calendar": "^0.19.0", "angular-in-memory-web-api": "^0.3.2", "core-js": "^2.4.1", "firebase": "^4.1.3", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 6ab85110..14c2b109 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -35,6 +35,10 @@ const appRoutes: Routes = [ path : 'apps/chat', loadChildren: './main/apps/chat/chat.module#ChatModule' }, + { + path : 'apps/calendar', + loadChildren: './main/apps/calendar/calendar.module#FuseCalendarModule' + }, { path : '**', redirectTo: 'apps/dashboards/project' @@ -60,8 +64,6 @@ const appRoutes: Routes = [ FuseLayoutModule, - // MailModule, - ChatModule, ProjectModule, UIPageLayoutsModule diff --git a/src/app/core/modules/shared.module.ts b/src/app/core/modules/shared.module.ts index 2b19e199..61cd37cf 100644 --- a/src/app/core/modules/shared.module.ts +++ b/src/app/core/modules/shared.module.ts @@ -11,6 +11,7 @@ import { FuseMdSidenavTogglerDirective } from '../directives/md-sidenav-helper/md-sidenav-helper.directive'; import { FusePipesModule } from '../pipes/pipes.module'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @NgModule({ declarations: [ diff --git a/src/app/main/apps/calendar/calendar.component.html b/src/app/main/apps/calendar/calendar.component.html new file mode 100644 index 00000000..adc1caa0 --- /dev/null +++ b/src/app/main/apps/calendar/calendar.component.html @@ -0,0 +1,117 @@ +
+ + +
+ +
+ +
+ + + + +
+ + + + + + + + + + +
+
+ + + +
+ + + +
+

{{ viewDate | calendarDate:(view + 'ViewTitle'):'en' }}

+ +
+ + +
+ +
+ + + + +
+ + + +
+
+ + + + + + +
+
+ +
diff --git a/src/app/main/apps/calendar/calendar.component.scss b/src/app/main/apps/calendar/calendar.component.scss new file mode 100644 index 00000000..3198312b --- /dev/null +++ b/src/app/main/apps/calendar/calendar.component.scss @@ -0,0 +1,126 @@ +@import "node_modules/angular-calendar/scss/angular-calendar"; + +#calendar { + background: #FFFFFF; + height: 100%; + + .header { + height: 200px; + min-height: 200px; + max-height: 200px; + padding: 24px; + position: relative; + background-size: 100% auto; + background-position: 0 50%; + background-repeat: no-repeat; + background-color: #FAFAFA; + color: #FFFFFF; + padding-bottom: 16px; + + &:before { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + background: rgba(0, 0, 0, 0.45); + } + + &.Jan { + background-image: url('/assets/images/backgrounds/january.jpg'); + background-position: 0 45%; + } + &.Feb { + background-image: url('/assets/images/backgrounds/february.jpg'); + background-position: 0 50%; + } + &.Mar { + background-image: url('/assets/images/backgrounds/march.jpg'); + background-position: 0 45%; + } + &.Apr { + background-image: url('/assets/images/backgrounds/april.jpg'); + background-position: 0 48%; + } + &.May { + background-image: url('/assets/images/backgrounds/may.jpg'); + background-position: 0 47%; + } + &.Jun { + background-image: url('/assets/images/backgrounds/june.jpg'); + background-position: 0 48%; + } + &.Jul { + background-image: url('/assets/images/backgrounds/july.jpg'); + background-position: 0 3%; + } + &.Aug { + background-image: url('/assets/images/backgrounds/august.jpg'); + background-position: 0 61%; + } + &.Sep { + background-image: url('/assets/images/backgrounds/september.jpg'); + background-position: 0 58%; + } + &.Oct { + background-image: url('/assets/images/backgrounds/october.jpg'); + background-position: 0 50%; + } + &.Nov { + background-image: url('/assets/images/backgrounds/november.jpg'); + background-position: 0 46%; + } + &.Dec { + background-image: url('/assets/images/backgrounds/december.jpg'); + background-position: 0 43%; + } + + .header-content { + height: 100%; + + .header-top { + position: relative; + z-index: 2; + + .logo { + + .logo-icon { + margin-right: 16px; + } + + .logo-text { + font-size: 24px; + } + } + } + + .header-bottom { + position: relative; + z-index: 2; + + .title { + font-size: 20px; + min-width: 160px; + text-align: center; + } + } + } + + .add-event-button { + position: absolute; + right: 18px; + bottom: -32px; + z-index: 10; + } + + md-icon { + color: #FFFFFF; + } + } + + .content { + padding: 24px; + } +} diff --git a/src/app/main/apps/calendar/calendar.component.ts b/src/app/main/apps/calendar/calendar.component.ts new file mode 100644 index 00000000..e04bb2fd --- /dev/null +++ b/src/app/main/apps/calendar/calendar.component.ts @@ -0,0 +1,188 @@ +import { + ChangeDetectionStrategy, + Component, + Inject, + OnInit, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { Subject } from 'rxjs/Subject'; +import { CalendarEvent, CalendarEventAction, CalendarEventTimesChangedEvent } from 'angular-calendar'; +import { MD_DIALOG_DATA, MdDialog, MdDialogRef } from '@angular/material'; +import { EventDialogComponent } from './event-dialog/event-dialog.component'; +import { + startOfDay, + endOfDay, + subDays, + addDays, + endOfMonth, + isSameDay, + isSameMonth, + addHours +} from 'date-fns'; + +const colors: any = { + red : { + primary : '#ad2121', + secondary: '#FAE3E3' + }, + blue : { + primary : '#1e90ff', + secondary: '#D1E8FF' + }, + yellow: { + primary : '#e3bc08', + secondary: '#FDF1BA' + } +}; + +@Component({ + selector : 'fuse-calendar', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl : './calendar.component.html', + styleUrls : ['./calendar.component.scss'], + encapsulation : ViewEncapsulation.None +}) +export class CalendarComponent implements OnInit +{ + @ViewChild('dialogContent') dialogContent: TemplateRef; + + view: string; + + viewDate: Date; + + events: CalendarEvent[]; + + actions: CalendarEventAction[]; + + activeDayIsOpen: boolean; + + refresh: Subject = new Subject(); + + dialogRef: any; + + constructor(public dialog: MdDialog) + { + this.view = 'month'; + this.viewDate = new Date(); + this.activeDayIsOpen = true; + + this.actions = [ + { + label : 'edit', + onClick: ({event}: { event: CalendarEvent }): void => { + this.handleEvent('Edited', event); + } + }, + { + label : 'close', + onClick: ({event}: { event: CalendarEvent }): void => { + this.events = this.events.filter(iEvent => iEvent !== event); + this.handleEvent('Deleted', event); + } + } + ]; + + this.events = [ + { + start : subDays(startOfDay(new Date()), 1), + end : addDays(new Date(), 1), + 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() + { + } + + dayClicked({date, events}: { date: Date; events: CalendarEvent[] }): void + { + if ( isSameMonth(date, this.viewDate) ) + { + if ( + (isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) || + events.length === 0 + ) + { + this.activeDayIsOpen = false; + } + else + { + this.activeDayIsOpen = true; + this.viewDate = date; + } + } + } + + eventTimesChanged({ + event, + newStart, + newEnd + }: CalendarEventTimesChangedEvent): void + { + event.start = newStart; + event.end = newEnd; + this.handleEvent('Dropped or resized', event); + this.refresh.next(); + } + + handleEvent(action: string, event: CalendarEvent): void + { + console.log(event, action); + this.dialogRef = this.dialog.open(EventDialogComponent, { + data: { + event : event, + action: action + } + }); + this.dialogRef.afterClosed().subscribe(result => { + console.info(result); + }); + } + + addEvent(): void + { + this.events.push({ + title : 'New event', + start : startOfDay(new Date()), + end : endOfDay(new Date()), + color : colors.red, + draggable: true, + resizable: { + beforeStart: true, + afterEnd : true + } + }); + this.refresh.next(); + } +} + + diff --git a/src/app/main/apps/calendar/calendar.module.ts b/src/app/main/apps/calendar/calendar.module.ts new file mode 100644 index 00000000..c9f4175c --- /dev/null +++ b/src/app/main/apps/calendar/calendar.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../../../core/modules/shared.module'; +import { RouterModule, Routes } from '@angular/router'; +import { CalendarComponent } from './calendar.component'; +import { CalendarService } from './calendar.service'; +import { CalendarModule } from 'angular-calendar'; +import { EventDialogComponent } from './event-dialog/event-dialog.component'; + +const routes: Routes = [ + { + path : '**', component: CalendarComponent, children: [], + resolve: { + chat: CalendarService + } + } +]; + +@NgModule({ + imports : [ + SharedModule, + RouterModule.forChild(routes), + CalendarModule.forRoot() + ], + declarations : [ + CalendarComponent, + EventDialogComponent, + ], + providers : [ + CalendarService + ], + entryComponents: [EventDialogComponent] +}) +export class FuseCalendarModule +{ +} diff --git a/src/app/main/apps/calendar/calendar.service.ts b/src/app/main/apps/calendar/calendar.service.ts new file mode 100644 index 00000000..5965a3da --- /dev/null +++ b/src/app/main/apps/calendar/calendar.service.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; +import { Http } from '@angular/http'; +import { Subject } from 'rxjs/Subject'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +@Injectable() +export class CalendarService implements Resolve +{ + + constructor(private http: Http) + { + } + + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | any + { + return new Promise((resolve, reject) => { + resolve(); + }); + } + +} diff --git a/src/app/main/apps/calendar/event-dialog/event-dialog.component.html b/src/app/main/apps/calendar/event-dialog/event-dialog.component.html new file mode 100644 index 00000000..7e80af2f --- /dev/null +++ b/src/app/main/apps/calendar/event-dialog/event-dialog.component.html @@ -0,0 +1,20 @@ +

+

+ + +
+
+ Action: +
{{ data.action }}
+
+
+ Event: +
{{ data.event | json }}
+
+
+
+ + +
diff --git a/src/app/main/apps/calendar/event-dialog/event-dialog.component.scss b/src/app/main/apps/calendar/event-dialog/event-dialog.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/main/apps/calendar/event-dialog/event-dialog.component.ts b/src/app/main/apps/calendar/event-dialog/event-dialog.component.ts new file mode 100644 index 00000000..76b6bc35 --- /dev/null +++ b/src/app/main/apps/calendar/event-dialog/event-dialog.component.ts @@ -0,0 +1,22 @@ +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, + @Inject(MD_DIALOG_DATA) private data: any) + { + console.log(data); + } + + ngOnInit() + { + } + +} diff --git a/src/app/main/apps/chat/chat.module.ts b/src/app/main/apps/chat/chat.module.ts index 284b9406..4cb3e4e5 100644 --- a/src/app/main/apps/chat/chat.module.ts +++ b/src/app/main/apps/chat/chat.module.ts @@ -13,7 +13,7 @@ import { ContactSidenavComponent } from './sidenavs/right/contact/contact.compon const routes: Routes = [ { - path : 'apps/chat', component: ChatComponent, children: [], + path : '**', component: ChatComponent, children: [], resolve: { chat: ChatService } From 0270405353ba08240e6b48c7bc8668303f98c1e4 Mon Sep 17 00:00:00 2001 From: mustafahlvc Date: Sun, 23 Jul 2017 18:27:17 +0300 Subject: [PATCH 4/5] (Chat App) Adding chat service function fixed --- src/app/main/apps/chat/chat.service.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/app/main/apps/chat/chat.service.ts b/src/app/main/apps/chat/chat.service.ts index 484603d8..78638753 100644 --- a/src/app/main/apps/chat/chat.service.ts +++ b/src/app/main/apps/chat/chat.service.ts @@ -88,10 +88,11 @@ export class ChatService implements Resolve }; const chatListItem = { - 'id' : chatId, - 'contactId' : contactId, - 'unread' : null, - 'lastMessageTime': '2017-02-18T10:30:18.931Z' + contactId : contactId, + id : chatId, + lastMessageTime: '2017-02-18T10:30:18.931Z', + name : contact.name, + unread : null, }; /** @@ -109,7 +110,7 @@ export class ChatService implements Resolve * Post the new the user data */ this.http.post('api/chat-user/' + this.user.id, this.user) - .subscribe(addedlistItem => { + .subscribe(newUserData => { /** * Update the user data from server From ed06e26647f021b3e2dc1a7c7636174779aa9eba Mon Sep 17 00:00:00 2001 From: mustafahlvc Date: Wed, 26 Jul 2017 10:02:26 +0300 Subject: [PATCH 5/5] (Calendar App) Calendar app added. --- package-lock.json | 5 + package.json | 1 + .../confirm-dialog.component.html | 6 + .../confirm-dialog.component.scss} | 0 .../confirm-dialog.component.ts | 21 ++ src/app/core/modules/shared.module.ts | 21 +- src/app/fuse-fake-db/calendar.ts | 97 ++++++ src/app/fuse-fake-db/fuse-fake-db.service.ts | 22 +- .../apps/calendar/calendar.component.html | 20 +- .../apps/calendar/calendar.component.scss | 177 +++++++++++ .../main/apps/calendar/calendar.component.ts | 296 +++++++++++------- src/app/main/apps/calendar/calendar.module.ts | 8 +- .../main/apps/calendar/calendar.service.ts | 37 ++- .../event-detail/event-detail.component.html | 3 + .../event-detail/event-detail.component.scss | 0 .../event-detail/event-detail.component.ts | 19 ++ .../event-dialog/event-dialog.component.html | 20 -- .../event-dialog/event-dialog.component.ts | 22 -- .../event-form/event-form.component.html | 139 ++++++++ .../event-form/event-form.component.scss | 12 + .../event-form/event-form.component.ts | 64 ++++ src/app/main/apps/calendar/event.model.ts | 75 +++++ 22 files changed, 890 insertions(+), 175 deletions(-) create mode 100644 src/app/core/components/confirm-dialog/confirm-dialog.component.html rename src/app/{main/apps/calendar/event-dialog/event-dialog.component.scss => core/components/confirm-dialog/confirm-dialog.component.scss} (100%) create mode 100644 src/app/core/components/confirm-dialog/confirm-dialog.component.ts create mode 100644 src/app/fuse-fake-db/calendar.ts create mode 100644 src/app/main/apps/calendar/event-detail/event-detail.component.html create mode 100644 src/app/main/apps/calendar/event-detail/event-detail.component.scss create mode 100644 src/app/main/apps/calendar/event-detail/event-detail.component.ts delete mode 100644 src/app/main/apps/calendar/event-dialog/event-dialog.component.html delete mode 100644 src/app/main/apps/calendar/event-dialog/event-dialog.component.ts create mode 100644 src/app/main/apps/calendar/event-form/event-form.component.html create mode 100644 src/app/main/apps/calendar/event-form/event-form.component.scss create mode 100644 src/app/main/apps/calendar/event-form/event-form.component.ts create mode 100644 src/app/main/apps/calendar/event.model.ts diff --git a/package-lock.json b/package-lock.json index 2aef8461..c529aa4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5929,6 +5929,11 @@ "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", "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": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/ngx-perfect-scrollbar/-/ngx-perfect-scrollbar-4.5.2.tgz", diff --git a/package.json b/package.json index 5ebed461..921a5530 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "core-js": "^2.4.1", "firebase": "^4.1.3", "hammerjs": "^2.0.8", + "ngx-color-picker": "^4.2.0", "ngx-perfect-scrollbar": "^4.5.2", "rxjs": "^5.4.2", "zone.js": "^0.8.13" diff --git a/src/app/core/components/confirm-dialog/confirm-dialog.component.html b/src/app/core/components/confirm-dialog/confirm-dialog.component.html new file mode 100644 index 00000000..d7d60191 --- /dev/null +++ b/src/app/core/components/confirm-dialog/confirm-dialog.component.html @@ -0,0 +1,6 @@ +

Confirm

+
{{confirmMessage}}
+
+ + +
diff --git a/src/app/main/apps/calendar/event-dialog/event-dialog.component.scss b/src/app/core/components/confirm-dialog/confirm-dialog.component.scss similarity index 100% rename from src/app/main/apps/calendar/event-dialog/event-dialog.component.scss rename to src/app/core/components/confirm-dialog/confirm-dialog.component.scss diff --git a/src/app/core/components/confirm-dialog/confirm-dialog.component.ts b/src/app/core/components/confirm-dialog/confirm-dialog.component.ts new file mode 100644 index 00000000..08bb4600 --- /dev/null +++ b/src/app/core/components/confirm-dialog/confirm-dialog.component.ts @@ -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) + { + } + + ngOnInit() + { + } + +} diff --git a/src/app/core/modules/shared.module.ts b/src/app/core/modules/shared.module.ts index 61cd37cf..164e6374 100644 --- a/src/app/core/modules/shared.module.ts +++ b/src/app/core/modules/shared.module.ts @@ -11,23 +11,26 @@ import { FuseMdSidenavTogglerDirective } from '../directives/md-sidenav-helper/md-sidenav-helper.directive'; 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({ - declarations: [ + declarations : [ FuseMdSidenavHelperDirective, - FuseMdSidenavTogglerDirective + FuseMdSidenavTogglerDirective, + FuseConfirmDialogComponent ], - imports : [ + imports : [ FlexLayoutModule, MaterialModule, CommonModule, FormsModule, FusePipesModule, PerfectScrollbarModule, - ReactiveFormsModule + ReactiveFormsModule, + ColorPickerModule ], - exports : [ + exports : [ FlexLayoutModule, MaterialModule, CommonModule, @@ -36,8 +39,10 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; FuseMdSidenavTogglerDirective, FusePipesModule, PerfectScrollbarModule, - ReactiveFormsModule - ] + ReactiveFormsModule, + ColorPickerModule + ], + entryComponents: [FuseConfirmDialogComponent] }) export class SharedModule diff --git a/src/app/fuse-fake-db/calendar.ts b/src/app/fuse-fake-db/calendar.ts new file mode 100644 index 00000000..fab7bc83 --- /dev/null +++ b/src/app/fuse-fake-db/calendar.ts @@ -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.' + } + } + ] + } + ]; +} diff --git a/src/app/fuse-fake-db/fuse-fake-db.service.ts b/src/app/fuse-fake-db/fuse-fake-db.service.ts index e8b48d5a..bc80d113 100644 --- a/src/app/fuse-fake-db/fuse-fake-db.service.ts +++ b/src/app/fuse-fake-db/fuse-fake-db.service.ts @@ -1,19 +1,31 @@ import { InMemoryDbService } from 'angular-in-memory-web-api'; 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 { createDb() { return { - 'mail-mails' : MailFakeDb.mails, - 'mail-folders': MailFakeDb.folders, - 'mail-filters': MailFakeDb.filters, - 'mail-labels' : MailFakeDb.labels, + 'mail-mails' : MailFakeDb.mails, + 'mail-folders' : MailFakeDb.folders, + 'mail-filters' : MailFakeDb.filters, + 'mail-labels' : MailFakeDb.labels, 'chat-contacts': ChatFakeDb.contacts, 'chat-chats' : ChatFakeDb.chats, 'chat-user' : ChatFakeDb.user, + 'calendar' : CalendarFakeDb.data }; } diff --git a/src/app/main/apps/calendar/calendar.component.html b/src/app/main/apps/calendar/calendar.component.html index adc1caa0..efaee667 100644 --- a/src/app/main/apps/calendar/calendar.component.html +++ b/src/app/main/apps/calendar/calendar.component.html @@ -25,7 +25,9 @@ @@ -54,19 +56,20 @@ mwlCalendarPreviousView [view]="view" [(viewDate)]="viewDate" + (viewDateChange)="selectedDay = {date:$event}" aria-label="Previous"> chevron_left
-

{{ viewDate | calendarDate:(view + 'ViewTitle'):'en' }}

- + {{ viewDate | calendarDate:(view + 'ViewTitle'):'en' }}
@@ -92,23 +95,28 @@ [refresh]="refresh" [activeDayIsOpen]="activeDayIsOpen" (dayClicked)="dayClicked($event.day)" - (eventClicked)="handleEvent('Clicked', $event.event)" - (eventTimesChanged)="eventTimesChanged($event)"> + (eventClicked)="editEvent('edit', $event.event)" + (eventTimesChanged)="eventTimesChanged($event)" + (beforeViewRender)="beforeMonthViewRender($event)"> diff --git a/src/app/main/apps/calendar/calendar.component.scss b/src/app/main/apps/calendar/calendar.component.scss index 3198312b..6e0d17bf 100644 --- a/src/app/main/apps/calendar/calendar.component.scss +++ b/src/app/main/apps/calendar/calendar.component.scss @@ -1,5 +1,179 @@ +@import "src/app/core/scss/fuse"; @import "node_modules/angular-calendar/scss/angular-calendar"; +.cal-month-view { + + .cal-header { + + .cal-cell { + font-weight: 500; + } + } + + .cal-day-cell { + + &.cal-open { + @include mat-elevation(3); + } + } + + .cal-open-day-events { + background: whitesmoke; + box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.13); + padding: 0; + display: flex; + flex-direction: column; + + > div { + padding: 0 16px; + margin: 8px 16px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: start; + background-color: white; + @include mat-elevation(1); + transition: box-shadow 300ms ease; + + &:first-of-type { + margin-top: 24px; + } + + &:last-of-type { + margin-bottom: 24px; + } + + &:hover { + @include mat-elevation(3); + } + + .cal-event { + top: 0; + margin: 0; + } + + mwl-calendar-event-title { + flex: 1; + + .cal-event-title { + display: block; + padding: 21px 24px; + line-height: 1; + text-decoration: none; + color: black; + } + } + + mwl-calendar-event-actions { + + .cal-event-actions { + display: flex; + flex-direction: row; + align-items: center; + + .cal-event-action { + display: block; + line-height: 1; + padding: 8px; + } + } + } + + } + } +} + +.cal-week-view { + + .cal-header > b { + font-weight: 500; + } + + .cal-event { + display: flex; + flex-direction: row; + align-items: center; + + mwl-calendar-event-title { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; + order: 0; + + .cal-event-title { + display: block; + //padding: 21px 24px; + line-height: 1; + text-decoration: none; + color: black; + } + } + + mwl-calendar-event-actions { + order: 1; + + .cal-event-actions { + display: flex; + flex-direction: row; + align-items: center; + + .cal-event-action { + display: block; + line-height: 1; + padding: 8px; + } + } + } + } +} + +.cal-day-view { + + .cal-time { + font-weight: 500; + } + + .cal-event { + display: flex; + flex-direction: row; + + mwl-calendar-event-title { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; + order: 0; + + .cal-event-title { + display: block; + height: 26px; + line-height: 26px; + text-decoration: none; + color: black; + } + } + + mwl-calendar-event-actions { + order: 1; + + .cal-event-actions { + display: flex; + flex-direction: row; + align-items: center; + + .cal-event-action { + display: block; + line-height: 1; + padding: 4px; + } + } + } + } +} + #calendar { background: #FFFFFF; height: 100%; @@ -104,6 +278,7 @@ font-size: 20px; min-width: 160px; text-align: center; + font-weight: 500; } } } @@ -121,6 +296,8 @@ } .content { + flex: 1; + overflow: auto; padding: 24px; } } diff --git a/src/app/main/apps/calendar/calendar.component.ts b/src/app/main/apps/calendar/calendar.component.ts index e04bb2fd..b208c028 100644 --- a/src/app/main/apps/calendar/calendar.component.ts +++ b/src/app/main/apps/calendar/calendar.component.ts @@ -1,41 +1,18 @@ -import { - ChangeDetectionStrategy, - Component, - Inject, - OnInit, - TemplateRef, - ViewChild, - ViewEncapsulation -} from '@angular/core'; +import { startOfDay, endOfDay, subDays, addDays, endOfMonth, isSameDay, isSameMonth, addHours } from 'date-fns'; +import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; import { Subject } from 'rxjs/Subject'; -import { CalendarEvent, CalendarEventAction, CalendarEventTimesChangedEvent } from 'angular-calendar'; -import { MD_DIALOG_DATA, MdDialog, MdDialogRef } from '@angular/material'; -import { EventDialogComponent } from './event-dialog/event-dialog.component'; +import { MdDialog, MdDialogRef } from '@angular/material'; +import { EventFormDialogComponent } from './event-form/event-form.component'; +import { FormGroup } from '@angular/forms'; +import { CalendarEventModel } from './event.model'; +import { CalendarService } from './calendar.service'; import { - startOfDay, - endOfDay, - subDays, - addDays, - endOfMonth, - isSameDay, - isSameMonth, - addHours -} from 'date-fns'; - -const colors: any = { - red : { - primary : '#ad2121', - secondary: '#FAE3E3' - }, - blue : { - primary : '#1e90ff', - secondary: '#D1E8FF' - }, - yellow: { - primary : '#e3bc08', - secondary: '#FDF1BA' - } -}; + CalendarEvent, + CalendarEventAction, + CalendarEventTimesChangedEvent, + CalendarMonthViewDay +} from 'angular-calendar'; +import { FuseConfirmDialogComponent } from '../../../core/components/confirm-dialog/confirm-dialog.component'; @Component({ selector : 'fuse-calendar', @@ -54,7 +31,7 @@ export class CalendarComponent implements OnInit events: CalendarEvent[]; - actions: CalendarEventAction[]; + public actions: CalendarEventAction[]; activeDayIsOpen: boolean; @@ -62,75 +39,106 @@ export class CalendarComponent implements OnInit dialogRef: any; - constructor(public dialog: MdDialog) + confirmDialogRef: MdDialogRef; + + selectedDay: any; + + constructor(public dialog: MdDialog, + public calendarService: CalendarService) { this.view = 'month'; this.viewDate = new Date(); this.activeDayIsOpen = true; + this.selectedDay = {date: startOfDay(new Date())}; this.actions = [ { label : 'edit', onClick: ({event}: { event: CalendarEvent }): void => { - this.handleEvent('Edited', event); + this.editEvent('edit', event); } }, { - label : 'close', + label : 'delete', onClick: ({event}: { event: CalendarEvent }): void => { - this.events = this.events.filter(iEvent => iEvent !== event); - this.handleEvent('Deleted', event); + this.deleteEvent(event); } } ]; - this.events = [ - { - start : subDays(startOfDay(new Date()), 1), - end : addDays(new Date(), 1), - 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 - } - ]; + /** + * Get events from service/server + */ + this.setEvents(); } ngOnInit() { + /** + * Watch re-render-refresh for updating db + */ + this.refresh.subscribe(updateDB => { + // console.warn('REFRESH'); + if ( updateDB ) + { + // console.warn('UPDATE DB'); + this.calendarService.updateEvents(this.events); + } + }); + + this.calendarService.onEventsUpdated.subscribe(events => { + this.setEvents(); + this.refresh.next(); + }); } - 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 ( - (isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) || - events.length === 0 - ) + if ( (isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) || events.length === 0 ) { this.activeDayIsOpen = false; } @@ -140,48 +148,120 @@ export class CalendarComponent implements OnInit this.viewDate = date; } } - } - - eventTimesChanged({ - event, - newStart, - newEnd - }: CalendarEventTimesChangedEvent): void - { - event.start = newStart; - event.end = newEnd; - this.handleEvent('Dropped or resized', event); + this.selectedDay = day; 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); - this.dialogRef = this.dialog.open(EventDialogComponent, { - data: { + event.start = newStart; + event.end = newEnd; + // console.warn('Dropped or resized', event); + this.refresh.next(true); + } + + /** + * Delete Event + * @param event + */ + deleteEvent(event) + { + this.confirmDialogRef = this.dialog.open(FuseConfirmDialogComponent, { + disableClose: false + }); + + this.confirmDialogRef.componentInstance.confirmMessage = 'Are you sure you want to delete?'; + + this.confirmDialogRef.afterClosed().subscribe(result => { + if ( result ) + { + const eventIndex = this.events.indexOf(event); + this.events.splice(eventIndex, 1); + this.refresh.next(true); + } + this.confirmDialogRef = null; + }); + + } + + /** + * Edit Event + * @param {string} action + * @param {CalendarEvent} event + */ + editEvent(action: string, event: CalendarEvent) + { + const eventIndex = this.events.indexOf(event); + + this.dialogRef = this.dialog.open(EventFormDialogComponent, { + panelClass: 'event-form-dialog', + data : { event : event, 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 { - this.events.push({ - title : 'New event', - start : startOfDay(new Date()), - end : endOfDay(new Date()), - color : colors.red, - draggable: true, - resizable: { - beforeStart: true, - afterEnd : true + this.dialogRef = this.dialog.open(EventFormDialogComponent, { + panelClass: 'event-form-dialog', + data : { + action: 'new', + date : this.selectedDay.date } }); - 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); + }); } } diff --git a/src/app/main/apps/calendar/calendar.module.ts b/src/app/main/apps/calendar/calendar.module.ts index c9f4175c..ca59bee0 100644 --- a/src/app/main/apps/calendar/calendar.module.ts +++ b/src/app/main/apps/calendar/calendar.module.ts @@ -4,7 +4,8 @@ import { RouterModule, Routes } from '@angular/router'; import { CalendarComponent } from './calendar.component'; import { CalendarService } from './calendar.service'; 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 = [ { @@ -23,12 +24,13 @@ const routes: Routes = [ ], declarations : [ CalendarComponent, - EventDialogComponent, + EventFormDialogComponent, + EventDetailDialogComponent, ], providers : [ CalendarService ], - entryComponents: [EventDialogComponent] + entryComponents: [EventFormDialogComponent, EventDetailDialogComponent] }) export class FuseCalendarModule { diff --git a/src/app/main/apps/calendar/calendar.service.ts b/src/app/main/apps/calendar/calendar.service.ts index 5965a3da..c765e74a 100644 --- a/src/app/main/apps/calendar/calendar.service.ts +++ b/src/app/main/apps/calendar/calendar.service.ts @@ -3,21 +3,52 @@ import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/r import { Observable } from 'rxjs/Observable'; import { Http } from '@angular/http'; import { Subject } from 'rxjs/Subject'; -import { BehaviorSubject } from 'rxjs/BehaviorSubject'; @Injectable() export class CalendarService implements Resolve { + events: any; + onEventsUpdated = new Subject(); constructor(private http: Http) { - } + } resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | any { return new Promise((resolve, reject) => { - resolve(); + Promise.all([ + this.getEvents() + ]).then( + ([events]: [any]) => { + resolve(); + }, + reject + ); + }); + } + + getEvents() + { + return new Promise((resolve, reject) => { + + this.http.get('api/calendar/events') + .subscribe(response => { + this.events = response.json().data.data; + this.onEventsUpdated.next(this.events); + resolve(this.events); + }, reject); + }); + } + + updateEvents(events) + { + return new Promise((resolve, reject) => { + this.http.post('api/calendar/events', {id: 'events', data: [...events]}) + .subscribe(response => { + this.getEvents(); + }, reject); }); } diff --git a/src/app/main/apps/calendar/event-detail/event-detail.component.html b/src/app/main/apps/calendar/event-detail/event-detail.component.html new file mode 100644 index 00000000..026b9043 --- /dev/null +++ b/src/app/main/apps/calendar/event-detail/event-detail.component.html @@ -0,0 +1,3 @@ +

+ event-detail works! +

diff --git a/src/app/main/apps/calendar/event-detail/event-detail.component.scss b/src/app/main/apps/calendar/event-detail/event-detail.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/main/apps/calendar/event-detail/event-detail.component.ts b/src/app/main/apps/calendar/event-detail/event-detail.component.ts new file mode 100644 index 00000000..e9c91f28 --- /dev/null +++ b/src/app/main/apps/calendar/event-detail/event-detail.component.ts @@ -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() + { + } + +} diff --git a/src/app/main/apps/calendar/event-dialog/event-dialog.component.html b/src/app/main/apps/calendar/event-dialog/event-dialog.component.html deleted file mode 100644 index 7e80af2f..00000000 --- a/src/app/main/apps/calendar/event-dialog/event-dialog.component.html +++ /dev/null @@ -1,20 +0,0 @@ -

-

- - -
-
- Action: -
{{ data.action }}
-
-
- Event: -
{{ data.event | json }}
-
-
-
- - -
diff --git a/src/app/main/apps/calendar/event-dialog/event-dialog.component.ts b/src/app/main/apps/calendar/event-dialog/event-dialog.component.ts deleted file mode 100644 index 76b6bc35..00000000 --- a/src/app/main/apps/calendar/event-dialog/event-dialog.component.ts +++ /dev/null @@ -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, - @Inject(MD_DIALOG_DATA) private data: any) - { - console.log(data); - } - - ngOnInit() - { - } - -} diff --git a/src/app/main/apps/calendar/event-form/event-form.component.html b/src/app/main/apps/calendar/event-form/event-form.component.html new file mode 100644 index 00000000..ab1141a2 --- /dev/null +++ b/src/app/main/apps/calendar/event-form/event-form.component.html @@ -0,0 +1,139 @@ + +
+ {{dialogTitle}} + +
+
+ +
+ +
+ + + + + +
+ + All Day + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + +
+ +
+ + + + + + + + + + + +
+ + + + + + + + + + +
+ +
+ +
+ + + + + + + +
diff --git a/src/app/main/apps/calendar/event-form/event-form.component.scss b/src/app/main/apps/calendar/event-form/event-form.component.scss new file mode 100644 index 00000000..ce618e53 --- /dev/null +++ b/src/app/main/apps/calendar/event-form/event-form.component.scss @@ -0,0 +1,12 @@ +.event-form-dialog { + .mat-dialog-container { + padding: 0; + max-width: 720px; + width: 720px; + } +} + +:host { + display: flex; + flex-direction: column; +} diff --git a/src/app/main/apps/calendar/event-form/event-form.component.ts b/src/app/main/apps/calendar/event-form/event-form.component.ts new file mode 100644 index 00000000..ccf7f713 --- /dev/null +++ b/src/app/main/apps/calendar/event-form/event-form.component.ts @@ -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, + @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) + }) + }); + } +} diff --git a/src/app/main/apps/calendar/event.model.ts b/src/app/main/apps/calendar/event.model.ts new file mode 100644 index 00000000..67008724 --- /dev/null +++ b/src/app/main/apps/calendar/event.model.ts @@ -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 || '' + }; + } + + +}