Compare commits

...

23 Commits

Author SHA1 Message Date
Sercan Yemen
01f00121ba Revert "removed md2 + replaced the date/time picker in scrumboard"
This reverts commit dde2333
2017-09-11 16:33:20 +03:00
Sercan Yemen
dde2333c8a removed md2
+ replaced the date/time picker in scrumboard
2017-09-11 16:19:46 +03:00
Sercan Yemen
babf6dc47b updated the readme 2017-09-11 13:06:24 +03:00
Sercan Yemen
55288bbbd4 set the correct default values for layout
+ increased the version number
2017-09-11 12:59:13 +03:00
Sercan Yemen
7297a9b4a4 toolbar adjustments 2017-09-11 12:34:16 +03:00
Sercan Yemen
4cbbd3a1d6 Merge branch 'master' of https://github.com/withinpixels/fuse2 2017-09-11 12:30:07 +03:00
Sercan Yemen
2f4ce6221e horizontal navigation layout
+ added boxed layout option
+ added a close overlay to theme options
+ moved the buy button to the footer
+ added fade-in-out animation
+ File Manager app min row height
2017-09-11 12:30:01 +03:00
mustafahlvc
44cdadaec9 (Scrumboard, Chat) some css style refinements 2017-09-08 19:21:35 +03:00
Sercan Yemen
d5b6dea1a2 update version 2017-08-30 20:52:08 +03:00
Sercan Yemen
31087186f7 Merge branch 'scrumboard-app' 2017-08-30 20:03:21 +03:00
Sercan Yemen
48a47a0876 sidenav helper small tweaks
+ chat & toolbar small mobile tweaks
2017-08-30 20:02:54 +03:00
Sercan Yemen
e0a85a19db package lock 2017-08-30 20:01:45 +03:00
mustafahlvc
b21526410a (Scrumboard) final touches.. 2017-08-30 18:46:07 +03:00
mustafahlvc
ce6797a80b (Scrumboard) Drag-drop support 2017-08-30 15:58:40 +03:00
mustafahlvc
7d5693421f Merge branch 'master' into scrumboard-app
# Conflicts:
#	package.json
2017-08-30 15:11:21 +03:00
mustafahlvc
9106fcd066 Merge branch 'master' into scrumboard-app
# Conflicts:
#	package.json
2017-08-30 15:11:07 +03:00
mustafahlvc
58bdf07da0 Style fixes especially for Edge/IE Browser 2017-08-30 15:04:20 +03:00
Sercan Yemen
58291058ae update version number 2017-08-30 14:42:55 +03:00
Sercan Yemen
9ae746a3ed fix: revert back to Angular 4.3.5 since the animations are not working correctly on 4.3.6 2017-08-30 14:36:54 +03:00
Sercan Yemen
a7549a1770 fix: pages not scrolling on various browsers 2017-08-30 14:26:45 +03:00
mustafahlvc
0bf158f8a5 (Scrumboard) board settings added + some refinements. 2017-08-30 14:21:14 +03:00
mustafahlvc
a0da9bd81e Merge branch 'master' into scrumboard-app
# Conflicts:
#	package.json
#	src/app/core/modules/shared.module.ts
2017-08-30 14:19:45 +03:00
mustafahlvc
cd99fbf77a (Scrumboard App) added
+ md2 added for date-time picker,
+ toggleInArray func added to FuseUtils
2017-08-30 06:54:11 +03:00
112 changed files with 5648 additions and 272 deletions

View File

@@ -1,6 +1,6 @@
# Fuse2
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.1.3.
Material Design Admin Template with Angular 4+ and Angular Material 2
## Development server
@@ -22,7 +22,3 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
Before running the tests make sure you are serving the app via `ng serve`.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

129
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "fuse2",
"version": "1.0.2",
"version": "1.0.4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -16,9 +16,9 @@
}
},
"@angular/animations": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.3.6.tgz",
"integrity": "sha1-v5KD7HyMmLMvVp2E3NoQiQ/cAmI=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.3.5.tgz",
"integrity": "sha1-hapFTIh8x8zhFjfqX26a+tiEkOE=",
"requires": {
"tslib": "1.7.1"
}
@@ -103,28 +103,28 @@
}
},
"@angular/common": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-4.3.6.tgz",
"integrity": "sha1-7TfpMHx1Bt2DR5fBps9nXlK1tu4=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-4.3.5.tgz",
"integrity": "sha1-X2sRNH6uHfw0YjzP1MBsj0xIji0=",
"requires": {
"tslib": "1.7.1"
}
},
"@angular/compiler": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.3.6.tgz",
"integrity": "sha1-vhcN8Ji3HoNczt8WjV+3sj5QRbg=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.3.5.tgz",
"integrity": "sha1-UNPJhmV77/H+9Pbdmj+ljiSr1Ug=",
"requires": {
"tslib": "1.7.1"
}
},
"@angular/compiler-cli": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-4.3.6.tgz",
"integrity": "sha1-avpq72jdaB5hs5i+TWJw5choCxI=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-4.3.5.tgz",
"integrity": "sha1-JOmbNsCQk2P/gke/MxqLiert/mM=",
"dev": true,
"requires": {
"@angular/tsc-wrapped": "4.3.6",
"@angular/tsc-wrapped": "4.3.5",
"minimist": "1.2.0",
"reflect-metadata": "0.1.10"
},
@@ -138,9 +138,9 @@
}
},
"@angular/core": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-4.3.6.tgz",
"integrity": "sha1-u6xj1o0Pe8s4nRKzQghlK+MofpY=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-4.3.5.tgz",
"integrity": "sha1-vR79vx68+5wnojjiqkxIFZsIlbs=",
"requires": {
"tslib": "1.7.1"
}
@@ -154,25 +154,25 @@
}
},
"@angular/forms": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.3.6.tgz",
"integrity": "sha1-DyDEWXwWoVJ0XXzZVVmFWgpcZoc=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.3.5.tgz",
"integrity": "sha1-UZqtCtgqG4cBmTf6k/wUdzRzd4c=",
"requires": {
"tslib": "1.7.1"
}
},
"@angular/http": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/http/-/http-4.3.6.tgz",
"integrity": "sha1-Vjgn0afV6J47fYa3f7vTZ7LAhZE=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/http/-/http-4.3.5.tgz",
"integrity": "sha1-gdSwdhyO8DXLC3NjAMI382KG8fA=",
"requires": {
"tslib": "1.7.1"
}
},
"@angular/language-service": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-4.3.6.tgz",
"integrity": "sha1-cc8tu0ZhVo89EqnA5LngQ++TvTo=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-4.3.5.tgz",
"integrity": "sha1-ttiC6kDRjVE/w6A1p5h1Ap/jjwE=",
"dev": true
},
"@angular/material": {
@@ -184,33 +184,33 @@
}
},
"@angular/platform-browser": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.3.6.tgz",
"integrity": "sha1-YVKx87eNAkb8XhUOL3ue1DN+O6Y=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.3.5.tgz",
"integrity": "sha1-wNA0CUmcwp+BZ3qrYjyQhnYNhO8=",
"requires": {
"tslib": "1.7.1"
}
},
"@angular/platform-browser-dynamic": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.3.6.tgz",
"integrity": "sha1-nqv4JvEZyY+Fwqlu3LGKsAtO+xw=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.3.5.tgz",
"integrity": "sha1-Sml7OwrsgFsziE/UqbNHMGW6seA=",
"requires": {
"tslib": "1.7.1"
}
},
"@angular/router": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-4.3.6.tgz",
"integrity": "sha1-ZAM+20/NoIoyPnUztKGCDA8o0TA=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-4.3.5.tgz",
"integrity": "sha1-GICCdHvJtpdPnUs/VVe0NGRdI80=",
"requires": {
"tslib": "1.7.1"
}
},
"@angular/tsc-wrapped": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@angular/tsc-wrapped/-/tsc-wrapped-4.3.6.tgz",
"integrity": "sha1-GqZuCrLEeZpK0UtnXhOVOqX81DY=",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@angular/tsc-wrapped/-/tsc-wrapped-4.3.5.tgz",
"integrity": "sha1-lf2qgTz8VyYvx+9f6nJtYorvq6w=",
"dev": true,
"requires": {
"tsickle": "0.21.6"
@@ -1544,26 +1544,15 @@
"dev": true
},
"connect": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/connect/-/connect-3.6.2.tgz",
"integrity": "sha1-aU6NIGgb/kkCgsiriGvpjwn0L+c=",
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/connect/-/connect-3.6.3.tgz",
"integrity": "sha512-GLSZqgjVxPvGYVD/2vz//gS201MEXk4b7t3nHV6OVnTdDNWi/Gm7Rpxs/ybvljPWvULys/wrzIV3jB3YvEc3nQ==",
"dev": true,
"requires": {
"debug": "2.6.7",
"finalhandler": "1.0.3",
"debug": "2.6.8",
"finalhandler": "1.0.4",
"parseurl": "1.3.1",
"utils-merge": "1.0.0"
},
"dependencies": {
"debug": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz",
"integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"connect-history-api-fallback": {
@@ -3143,29 +3132,18 @@
}
},
"finalhandler": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz",
"integrity": "sha1-70fneVDpmXgOhgIqVg4yF+DQzIk=",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz",
"integrity": "sha512-16l/r8RgzlXKmFOhZpHBztvye+lAhC5SU7hXavnerC9UfZqZxxXl3BzL8MhffPT3kF61lj9Oav2LKEzh0ei7tg==",
"dev": true,
"requires": {
"debug": "2.6.7",
"debug": "2.6.8",
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
"on-finished": "2.3.0",
"parseurl": "1.3.1",
"statuses": "1.3.1",
"unpipe": "1.0.0"
},
"dependencies": {
"debug": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz",
"integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"find-up": {
@@ -5545,9 +5523,9 @@
}
},
"karma": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/karma/-/karma-1.7.0.tgz",
"integrity": "sha1-b3oaQGRG+i4YfslTmGmPTO5HYmk=",
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/karma/-/karma-1.7.1.tgz",
"integrity": "sha512-k5pBjHDhmkdaUccnC7gE3mBzZjcxyxYsYVaqiL2G5AqlfLyBO5nw2VdNK+O16cveEPd/gIOWULH7gkiYYwVNHg==",
"dev": true,
"requires": {
"bluebird": "3.5.0",
@@ -5555,7 +5533,7 @@
"chokidar": "1.7.0",
"colors": "1.1.2",
"combine-lists": "1.0.1",
"connect": "3.6.2",
"connect": "3.6.3",
"core-js": "2.5.0",
"di": "0.0.1",
"dom-serialize": "2.2.1",
@@ -5939,6 +5917,11 @@
"integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=",
"dev": true
},
"md2": {
"version": "0.0.28",
"resolved": "https://registry.npmjs.org/md2/-/md2-0.0.28.tgz",
"integrity": "sha512-XQ71eTVKG3oRsGBj3lMLqL8p2inueqDXn++a2EntzWkUPlBZXPCPtlpfI9ER/LAlBKwJZQSqTzFItw7q9+vgvw=="
},
"md5.js": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "fuse2",
"version": "1.0.2",
"version": "1.0.5",
"license": "",
"scripts": {
"ng": "ng",
@@ -12,18 +12,18 @@
},
"private": true,
"dependencies": {
"@angular/animations": "4.3.6",
"@angular/animations": "4.3.5",
"@angular/cdk": "2.0.0-beta.10",
"@angular/common": "4.3.6",
"@angular/compiler": "4.3.6",
"@angular/core": "4.3.6",
"@angular/common": "4.3.5",
"@angular/compiler": "4.3.5",
"@angular/core": "4.3.5",
"@angular/flex-layout": "2.0.0-beta.9",
"@angular/forms": "4.3.6",
"@angular/http": "4.3.6",
"@angular/forms": "4.3.5",
"@angular/http": "4.3.5",
"@angular/material": "2.0.0-beta.10",
"@angular/platform-browser": "4.3.6",
"@angular/platform-browser-dynamic": "4.3.6",
"@angular/router": "4.3.6",
"@angular/platform-browser": "4.3.5",
"@angular/platform-browser-dynamic": "4.3.5",
"@angular/router": "4.3.5",
"@swimlane/ngx-charts": "6.0.2",
"@swimlane/ngx-datatable": "9.3.1",
"@swimlane/ngx-dnd": "3.0.0",
@@ -35,6 +35,7 @@
"hammerjs": "2.0.8",
"highlight.js": "9.12.0",
"intl": "1.2.5",
"md2": "0.0.28",
"moment": "2.18.1",
"ngx-color-picker": "4.3.1",
"ngx-cookie-service": "1.0.7",
@@ -45,8 +46,8 @@
},
"devDependencies": {
"@angular/cli": "^1.3.2",
"@angular/compiler-cli": "4.3.6",
"@angular/language-service": "4.3.6",
"@angular/compiler-cli": "4.3.5",
"@angular/language-service": "4.3.5",
"@ngtools/webpack": "^1.6.2",
"@types/jasmine": "^2.5.54",
"@types/jasminewd2": "^2.0.2",
@@ -54,7 +55,7 @@
"codelyzer": "~3.0.1",
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~1.7.0",
"karma": "1.7.1",
"karma-chrome-launcher": "~2.1.1",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1",

View File

@@ -45,6 +45,10 @@ const appRoutes: Routes = [
path : 'apps/contacts',
loadChildren: './main/content/apps/contacts/contacts.module#FuseContactsModule'
},
{
path : 'apps/scrumboard',
loadChildren: './main/content/apps/scrumboard/scrumboard.module#FuseScrumboardModule'
},
{
path : '**',
redirectTo: 'apps/dashboards/project'

View File

@@ -4,6 +4,19 @@ import { sequence, trigger, stagger, animate, style, group, query, transition, k
export class Animations
{
public static fadeInOut = trigger('fadeInOut', [
state('0', style({
display: 'none',
opacity: 0
})),
state('1', style({
display: 'block',
opacity: 1
})),
transition('1 => 0', animate('300ms ease-out')),
transition('0 => 1', animate('300ms ease-in'))
]);
public static slideInOut = trigger('slideInOut', [
state('0', style({
height : '0px',

View File

@@ -1,4 +1,5 @@
<button md-icon-button
type="button"
class="mat-elevation-z1"
[mdMenuTriggerFor]="colorMenu"
(onMenuOpen)="onMenuOpen()"

View File

@@ -0,0 +1,19 @@
<a class="nav-link" md-ripple>
<md-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</md-icon>
<span class="nav-link-title">{{item.title}}</span>
<md-icon class="collapse-arrow">keyboard_arrow_right</md-icon>
</a>
<div class="children" [ngClass]="{'open': isOpen}">
<div class="{{fuseSettings.colorClasses.navbar}}">
<ng-container *ngFor="let item of item.children">
<fuse-nav-horizontal-item *ngIf="item.type=='nav-item'" [item]="item"></fuse-nav-horizontal-item>
<fuse-nav-horizontal-collapse *ngIf="item.type=='nav-collapse'"
[item]="item"></fuse-nav-horizontal-collapse>
</ng-container>
</div>
</div>

View File

@@ -0,0 +1,50 @@
import { Component, HostBinding, HostListener, Input, OnDestroy } from '@angular/core';
import { Animations } from '../../../../animations';
import { FuseConfigService } from '../../../../services/config.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector : 'fuse-nav-horizontal-collapse',
templateUrl: './nav-horizontal-collapse.component.html',
styleUrls : ['./nav-horizontal-collapse.component.scss'],
animations : [Animations.slideInOut]
})
export class FuseNavHorizontalCollapseComponent implements OnDestroy
{
onSettingsChanged: Subscription;
fuseSettings: any;
isOpen = false;
@HostBinding('class') classes = 'nav-item nav-collapse';
@Input() item: any;
@HostListener('mouseenter')
open()
{
this.isOpen = true;
}
@HostListener('mouseleave')
close()
{
this.isOpen = false;
}
constructor(
private fuseConfig: FuseConfigService
)
{
this.onSettingsChanged =
this.fuseConfig.onSettingsChanged
.subscribe(
(newSettings) => {
this.fuseSettings = newSettings;
}
);
}
ngOnDestroy()
{
this.onSettingsChanged.unsubscribe();
}
}

View File

@@ -0,0 +1,6 @@
<a class="nav-link" md-ripple
[routerLink]="[item.url]" routerLinkActive="active">
<md-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</md-icon>
<span class="nav-link-title">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">{{item.badge.title}}</span>
</a>

View File

@@ -0,0 +1,12 @@
import { Component, HostBinding, Input } from '@angular/core';
@Component({
selector : 'fuse-nav-horizontal-item',
templateUrl: './nav-horizontal-item.component.html',
styleUrls : ['./nav-horizontal-item.component.scss']
})
export class FuseNavHorizontalItemComponent
{
@HostBinding('class') classes = 'nav-item';
@Input() item: any;
}

View File

@@ -1,21 +0,0 @@
import { Component, HostBinding, Input, OnInit } from '@angular/core';
@Component({
selector : 'fuse-nav-subheader',
templateUrl: './nav-subheader.component.html',
styleUrls : ['./nav-subheader.component.scss']
})
export class FuseNavSubheaderComponent implements OnInit
{
@HostBinding('class') classes = 'nav-subheader';
@Input() item: any;
constructor()
{
}
ngOnInit()
{
}
}

View File

@@ -1,11 +1,27 @@
<div id="main-navigation" class="nav">
<ng-container *ngFor="let item of navigation">
<div id="main-navigation" class="nav" [ngClass]="{'horizontal':layout === 'horizontal'}">
<fuse-nav-subheader *ngIf="item.type=='subheader'" [item]="item"></fuse-nav-subheader>
<ng-container *ngIf="layout === 'vertical'">
<fuse-nav-item *ngIf="item.type=='nav-item'" [item]="item"></fuse-nav-item>
<ng-container *ngFor="let item of verticalNavigation">
<fuse-nav-collapse *ngIf="item.type=='nav-collapse'" [item]="item"></fuse-nav-collapse>
<fuse-nav-vertical-subheader *ngIf="item.type=='subheader'" [item]="item"></fuse-nav-vertical-subheader>
<fuse-nav-vertical-item *ngIf="item.type=='nav-item'" [item]="item"></fuse-nav-vertical-item>
<fuse-nav-vertical-collapse *ngIf="item.type=='nav-collapse'" [item]="item"></fuse-nav-vertical-collapse>
</ng-container>
</ng-container>
<ng-container *ngIf="layout === 'horizontal'">
<ng-container *ngFor="let item of horizontalNavigation">
<fuse-nav-horizontal-item *ngIf="item.type=='nav-item'" [item]="item"></fuse-nav-horizontal-item>
<fuse-nav-horizontal-collapse *ngIf="item.type=='nav-collapse'"
[item]="item"></fuse-nav-horizontal-collapse>
</ng-container>
</ng-container>
</div>

View File

@@ -1,4 +1,4 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { Component, Input, ViewEncapsulation } from '@angular/core';
import { FuseNavigationService } from './navigation.service';
@Component({
@@ -9,11 +9,15 @@ import { FuseNavigationService } from './navigation.service';
})
export class FuseNavigationComponent
{
navigation: any[];
verticalNavigation: any[];
horizontalNavigation: any[];
@Input('layout') layout = 'vertical';
constructor(private navigationService: FuseNavigationService)
{
this.navigation = navigationService.getNavigation();
this.verticalNavigation = navigationService.getNavigation('verticalNavItems');
this.horizontalNavigation = navigationService.getNavigation('horizontalNavItems');
}
}

View File

@@ -1,10 +1,12 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../../modules/shared.module';
import { RouterModule } from '@angular/router';
import { FuseNavSubheaderComponent } from './nav-subheader/nav-subheader.component';
import { FuseNavigationComponent } from './navigation.component';
import { FuseNavItemComponent } from './nav-item/nav-item.component';
import { FuseNavCollapseComponent } from './nav-collapse/nav-collapse.component';
import { FuseNavVerticalItemComponent } from './vertical/nav-item/nav-vertical-item.component';
import { FuseNavVerticalCollapseComponent } from './vertical/nav-collapse/nav-vertical-collapse.component';
import { FuseNavVerticalSubheaderComponent } from './vertical/nav-subheader/nav-vertical-subheader.component';
import { FuseNavHorizontalItemComponent } from './horizontal/nav-item/nav-horizontal-item.component';
import { FuseNavHorizontalCollapseComponent } from './horizontal/nav-collapse/nav-horizontal-collapse.component';
@NgModule({
imports : [
@@ -16,9 +18,11 @@ import { FuseNavCollapseComponent } from './nav-collapse/nav-collapse.component'
],
declarations: [
FuseNavigationComponent,
FuseNavSubheaderComponent,
FuseNavItemComponent,
FuseNavCollapseComponent
FuseNavVerticalSubheaderComponent,
FuseNavVerticalItemComponent,
FuseNavVerticalCollapseComponent,
FuseNavHorizontalItemComponent,
FuseNavHorizontalCollapseComponent
]
})
export class FuseNavigationModule

View File

@@ -5,21 +5,21 @@ import { FuseNavigation } from '../../../navigation.model';
export class FuseNavigationService
{
onNavCollapseToggled = new EventEmitter<any>();
navigation: any[];
navigation: FuseNavigation;
flatNavigation: any[] = [];
constructor()
{
this.navigation = new FuseNavigation().items;
this.navigation = new FuseNavigation();
}
/**
* Get navigation array
* @returns {any[]}
*/
getNavigation()
getNavigation(item)
{
return this.navigation;
return this.navigation[item];
}
/**

View File

@@ -5,7 +5,7 @@
</a>
<div class="children" [@slideInOut]="isOpen">
<ng-container *ngFor="let item of item.children">
<fuse-nav-item *ngIf="item.type=='nav-item'" [item]="item"></fuse-nav-item>
<fuse-nav-collapse *ngIf="item.type=='nav-collapse'" [item]="item"></fuse-nav-collapse>
<fuse-nav-vertical-item *ngIf="item.type=='nav-item'" [item]="item"></fuse-nav-vertical-item>
<fuse-nav-vertical-collapse *ngIf="item.type=='nav-collapse'" [item]="item"></fuse-nav-vertical-collapse>
</ng-container>
</div>

View File

@@ -1,15 +1,15 @@
import { Component, HostBinding, Input, OnInit } from '@angular/core';
import { FuseNavigationService } from '../navigation.service';
import { FuseNavigationService } from '../../navigation.service';
import { NavigationEnd, Router } from '@angular/router';
import { Animations } from '../../../animations';
import { Animations } from '../../../../animations';
@Component({
selector : 'fuse-nav-collapse',
templateUrl: './nav-collapse.component.html',
styleUrls : ['./nav-collapse.component.scss'],
selector : 'fuse-nav-vertical-collapse',
templateUrl: './nav-vertical-collapse.component.html',
styleUrls : ['./nav-vertical-collapse.component.scss'],
animations : [Animations.slideInOut]
})
export class FuseNavCollapseComponent implements OnInit
export class FuseNavVerticalCollapseComponent implements OnInit
{
@Input() item: any;
@HostBinding('class') classes = 'nav-collapse nav-item';

View File

@@ -1,11 +1,11 @@
import { Component, HostBinding, Input, OnInit } from '@angular/core';
@Component({
selector : 'fuse-nav-item',
templateUrl: './nav-item.component.html',
styleUrls : ['./nav-item.component.scss']
selector : 'fuse-nav-vertical-item',
templateUrl: './nav-vertical-item.component.html',
styleUrls : ['./nav-vertical-item.component.scss']
})
export class FuseNavItemComponent implements OnInit
export class FuseNavVerticalItemComponent implements OnInit
{
@HostBinding('class') classes = 'nav-item';
@Input() item: any;

View File

@@ -0,0 +1,21 @@
import { Component, HostBinding, Input, OnInit } from '@angular/core';
@Component({
selector : 'fuse-nav-vertical-subheader',
templateUrl: './nav-vertical-subheader.component.html',
styleUrls : ['./nav-vertical-subheader.component.scss']
})
export class FuseNavVerticalSubheaderComponent implements OnInit
{
@HostBinding('class') classes = 'nav-subheader';
@Input() item: any;
constructor()
{
}
ngOnInit()
{
}
}

View File

@@ -3,11 +3,14 @@
:host {
.fuse-search-bar {
min-width: 64px;
height: 64px;
font-size: 13px;
@include media-breakpoint-down('xs') {
@include media-breakpoint-down('sm') {
height: 56px;
}
.fuse-search-bar-expander,
.fuse-search-bar-collapser {
cursor: pointer;
@@ -16,7 +19,8 @@
width: 64px !important;
height: 64px !important;
line-height: 64px !important;
@include media-breakpoint-down('xs') {
@include media-breakpoint-down('sm') {
height: 56px !important;
line-height: 56px !important;
}
@@ -26,7 +30,7 @@
width: 64px !important;
height: 64px !important;
line-height: 64px !important;
@include media-breakpoint-down('xs') {
@include media-breakpoint-down('sm') {
height: 56px !important;
line-height: 56px !important;
}
@@ -70,4 +74,4 @@
}
}
}
}
}

View File

@@ -9,7 +9,7 @@
<div class="shortcuts" fxHide fxShow.gt-sm [ngClass]="toolbarColor">
<div fxLayout="row" fxLayoutAlign="space-between center" fxFlex>
<div fxLayout="row" fxLayoutAlign="space-between center" fxFlex="0 1 auto">
<div fxLayout="row" fxLayoutAlign="start center">

View File

@@ -2,7 +2,9 @@
<md-icon>settings</md-icon>
</button>
<div #panel class="theme-options-panel md-white-bg mat-elevation-z2 pb-16">
<div class="theme-options-panel-overlay" #overlay [fxHide]="barClosed" [@fadeInOut]="!barClosed"></div>
<div #panel class="theme-options-panel md-white-bg mat-elevation-z8 p-16">
<button md-icon-button class="close-button" (click)="closeBar()">
<md-icon>close</md-icon>
@@ -12,6 +14,7 @@
<h3 md-subheader>Navigation:</h3>
<md-list-item>
<md-radio-group [(ngModel)]="fuseSettings.layout.navigation" (ngModelChange)="onSettingsChange()">
<md-radio-button class="mr-8" value="top">Top</md-radio-button>
<md-radio-button class="mr-8" value="left">Left</md-radio-button>
<md-radio-button class="mr-8" value="right">Right</md-radio-button>
<md-radio-button class="mr-8" value="none">None</md-radio-button>
@@ -36,25 +39,33 @@
</md-radio-group>
</md-list-item>
<h3 md-subheader>Layout Mode:</h3>
<md-list-item>
<md-radio-group [(ngModel)]="fuseSettings.layout.mode" (ngModelChange)="onSettingsChange()">
<md-radio-button class="mr-8" value="boxed">Boxed</md-radio-button>
<md-radio-button class="mr-8" value="fullwidth">Fullwidth</md-radio-button>
</md-radio-group>
</md-list-item>
<md-divider></md-divider>
<h3 md-subheader>Colors:</h3>
<md-list-item>
<md-list-item class="mb-8">
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<h4>Toolbar Color</h4>
<fuse-material-color-picker [(selectedClass)]="fuseSettings.colorClasses.toolbar" (onValueChange)="onSettingsChange()"></fuse-material-color-picker>
</div>
</md-list-item>
<md-list-item>
<md-list-item class="mb-8">
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<h4>Navigation Bar Color</h4>
<fuse-material-color-picker [(selectedClass)]="fuseSettings.colorClasses.navbar" (onValueChange)="onSettingsChange()"></fuse-material-color-picker>
</div>
</md-list-item>
<md-list-item>
<md-list-item class="mb-8">
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<h4>Footer Color</h4>
<fuse-material-color-picker [(selectedClass)]="fuseSettings.colorClasses.footer" (onValueChange)="onSettingsChange()"></fuse-material-color-picker>
@@ -68,7 +79,7 @@
<md-list-item>
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<h4>Router Animation</h4>
<md-select [(ngModel)]="fuseSettings.routerAnimation">
<md-select class="p-0" [(ngModel)]="fuseSettings.routerAnimation">
<md-option value="none">
None
</md-option>

View File

@@ -19,14 +19,29 @@
position: absolute;
right: 0;
top: 0;
width: 320px;
width: 360px;
transform: translate3d(100%, 0, 0);
z-index: 999;
.close-button {
position: absolute;
top: 0;
right: 0;
top: 8px;
right: 8px;
}
}
.theme-options-panel-overlay {
position: fixed;
display: block;
background: rgba(0, 0, 0, 0);
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: 998;
&.hidden {
display: none;
}
}
@@ -34,6 +49,10 @@
font-size: 15px;
}
.mat-divider {
margin: 16px;
}
.open-button {
position: absolute;
top: 0;

View File

@@ -1,28 +1,35 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Component, ElementRef, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core';
import { style, animate, AnimationBuilder, AnimationPlayer } from '@angular/animations';
import { Subscription } from 'rxjs/Subscription';
import { FuseConfigService } from '../../services/config.service';
import { Animations } from '../../animations';
@Component({
selector : 'fuse-theme-options',
templateUrl: './theme-options.component.html',
styleUrls : ['./theme-options.component.scss']
styleUrls : ['./theme-options.component.scss'],
animations : [Animations.fadeInOut]
})
export class FuseThemeOptionsComponent implements OnInit, OnDestroy
{
@ViewChild('openButton') openButton;
@ViewChild('panel') panel;
@ViewChild('overlay') overlay: ElementRef;
public player: AnimationPlayer;
fuseSettings: any;
barClosed: boolean;
onSettingsChanged: Subscription;
constructor(
private animationBuilder: AnimationBuilder,
private fuseConfig: FuseConfigService
private fuseConfig: FuseConfigService,
private renderer: Renderer2
)
{
this.barClosed = true;
this.onSettingsChanged =
this.fuseConfig.onSettingsChanged
.subscribe(
@@ -34,6 +41,9 @@ export class FuseThemeOptionsComponent implements OnInit, OnDestroy
ngOnInit()
{
this.renderer.listen(this.overlay.nativeElement, 'click', () => {
this.closeBar();
});
}
onSettingsChange()
@@ -43,23 +53,29 @@ export class FuseThemeOptionsComponent implements OnInit, OnDestroy
closeBar()
{
this.barClosed = true;
this.player =
this.animationBuilder
.build([
style({transform: 'translate3d(0,0,0)'}),
animate('400ms ease', style({transform: 'translate3d(100%,0,0)'}))
]).create(this.panel.nativeElement);
this.player.play();
}
openBar()
{
this.barClosed = false;
this.player =
this.animationBuilder
.build([
style({transform: 'translate3d(100%,0,0)'}),
animate('400ms ease', style({transform: 'translate3d(0,0,0)'}))
]).create(this.panel.nativeElement);
this.player.play();
}

View File

@@ -33,17 +33,17 @@ export class FuseMdSidenavHelperDirective implements OnInit, AfterViewInit, OnDe
if ( this.observableMedia.isActive(this.mdIsLockedOpenBreakpoint) )
{
this.isLockedOpen = true;
this.mdSidenav.mode = 'side';
setTimeout(() => {
this.isLockedOpen = true;
this.mdSidenav.mode = 'side';
this.mdSidenav.open();
});
}
else
{
this.isLockedOpen = false;
this.mdSidenav.mode = 'over';
setTimeout(() => {
this.isLockedOpen = false;
this.mdSidenav.mode = 'over';
this.mdSidenav.close();
});
}
@@ -51,15 +51,19 @@ export class FuseMdSidenavHelperDirective implements OnInit, AfterViewInit, OnDe
this.matchMediaSubscription = this.fuseMatchMedia.onMediaChange.subscribe(() => {
if ( this.observableMedia.isActive(this.mdIsLockedOpenBreakpoint) )
{
this.isLockedOpen = true;
this.mdSidenav.mode = 'side';
this.mdSidenav.open();
setTimeout(() => {
this.isLockedOpen = true;
this.mdSidenav.mode = 'side';
this.mdSidenav.open();
});
}
else
{
this.isLockedOpen = false;
this.mdSidenav.mode = 'over';
this.mdSidenav.close();
setTimeout(() => {
this.isLockedOpen = false;
this.mdSidenav.mode = 'over';
this.mdSidenav.close();
});
}
});

View File

@@ -90,4 +90,16 @@ export class FuseUtils
return (S4() + S4());
}
public static toggleInArray(item, array)
{
if ( array.indexOf(item) === -1 )
{
array.push(item);
}
else
{
array.splice(array.indexOf(item), 1);
}
}
}

View File

@@ -15,11 +15,12 @@ import { FuseConfirmDialogComponent } from '../components/confirm-dialog/confirm
import { FuseCountdownComponent } from '../components/countdown/countdown.component';
import { FuseNavigationService } from '../components/navigation/navigation.service';
import { FuseMatchMedia } from '../services/match-media.service';
import { FuseNavbarService } from '../../main/navbar/navbar.service';
import { FuseNavbarVerticalService } from '../../main/navbar/vertical/navbar-vertical.service';
import { FuseMdSidenavHelperService } from '../directives/md-sidenav-helper/md-sidenav-helper.service';
import { FuseHljsComponent } from '../components/hljs/hljs.component';
import { FuseIfOnDomDirective } from '../directives/fuse-if-on-dom/fuse-if-on-dom.directive';
import { FuseMaterialColorPickerComponent } from '../components/material-color-picker/material-color-picker.component';
import { Md2Module } from 'md2';
import { CookieService } from 'ngx-cookie-service';
@NgModule({
@@ -42,7 +43,8 @@ import { CookieService } from 'ngx-cookie-service';
ReactiveFormsModule,
ColorPickerModule,
NgxDnDModule,
NgxDatatableModule
NgxDatatableModule,
Md2Module
],
exports : [
FlexLayoutModule,
@@ -60,7 +62,8 @@ import { CookieService } from 'ngx-cookie-service';
NgxDnDModule,
NgxDatatableModule,
FuseIfOnDomDirective,
FuseMaterialColorPickerComponent
FuseMaterialColorPickerComponent,
Md2Module
],
entryComponents: [
FuseConfirmDialogComponent
@@ -69,7 +72,7 @@ import { CookieService } from 'ngx-cookie-service';
CookieService,
FuseNavigationService,
FuseMatchMedia,
FuseNavbarService,
FuseNavbarVerticalService,
FuseMdSidenavHelperService
]
})

View File

@@ -1,6 +1,9 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'getById'})
@Pipe({
name: 'getById',
pure: false
})
export class GetByIdPipe implements PipeTransform
{
transform(value: any[], id: number, property: string): any

View File

@@ -54,7 +54,8 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
&.secondary-text,
.secondary-text,
.mat-icon,
.icon {
.icon,
.md2-datepicker-button {
color: rgba(0, 0, 0, 0.54);
}
@@ -79,7 +80,8 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
@else {
.mat-icon,
.icon {
.icon,
.md2-datepicker-button {
color: rgba(255, 255, 255, 1);
}

View File

@@ -118,4 +118,54 @@
}
}
}
&.horizontal {
display: flex;
flex-direction: row;
.nav-item {
&.nav-collapse {
position: relative;
.children {
display: none;
position: absolute;
top: 0;
left: 100%;
z-index: 999;
min-width: 200px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, .2), 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .12);
&.open {
display: block;
}
.nav-link {
padding-left: 24px !important;
}
}
}
}
> .nav-item {
&.nav-collapse {
position: relative;
> .nav-link {
height: 56px;
.collapse-arrow {
display: none;
}
}
> .children {
top: 100%;
left: 0;
}
}
}
}
}

View File

@@ -0,0 +1,360 @@
/*@font-face {
font-family: 'fontello';
src: url('../font/fontello.eot?81091010');
src: url('../font/fontello.eot?81091010#iefix') format('embedded-opentype'),
url('../font/fontello.woff2?81091010') format('woff2'),
url('../font/fontello.woff?81091010') format('woff'),
url('../font/fontello.ttf?81091010') format('truetype'),
url('../font/fontello.svg?81091010#fontello') format('svg');
font-weight: normal;
font-style: normal;
}*/
owl-date-time {
[class^="icon-"]:before, [class*=" icon-"]:before {
font-family: "Material Icons";
font-style: normal;
font-weight: normal;
speak: none;
display: flex;
align-items: center;
justify-content: center;
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-cancel:before {
content: 'close';
}
.icon-up-open:before {
content: 'keyboard_arrow_up';
}
.icon-down-open:before {
content: 'keyboard_arrow_down';
}
.icon-left-open:before {
content: 'chevron_left';
}
.icon-right-open:before {
content: 'chevron_right';
}
$white: #FFFFFF;
$black: #000000;
$grey: #DDDDDD;
$blue: #0070BA;
.owl-widget,
.owl-widget * {
box-sizing: border-box;
}
.owl-widget {
font-size: 1em;
}
.owl-state-focus {
}
.owl-corner-all {
border-radius: 2px;
}
.owl-corner-top {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.owl-state-default {
background: #FFFFFF;
color: rgba(0, 0, 0, 0.87);
}
.owl-dateTime-inputWrapper {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
.owl-dateTime-input {
background: none !important;
padding: 0 !important;
cursor: pointer;
.owl-inputtext {
margin: 0;
padding: 8px;
background: none !important;
color: rgba(0, 0, 0, 0.87);
}
}
.owl-dateTime-cancel {
position: relative !important;
right: 0 !important;
top: 0 !important;
transform: none !important;
font-size: 16px !important;
width: 16px !important;
height: 16px !important;
min-width: 16px !important;
min-height: 16px !important;
line-height: 16px !important;
color: rgba(0, 0, 0, 0.54) !important;
}
}
.owl-dateTime {
position: relative;
width: 140px;
&.owl-dateTime-inline {
width: auto;
.owl-dateTime-dialog {
position: relative;
z-index: auto;
}
}
}
.owl-dateTime-dialog {
width: 256px;
user-select: none;
z-index: 99999;
top: 24px !important;
right: 0 !important;
left: auto !important;
@include mat-elevation(4);
}
.owl-dateTime-dialogHeader {
height: 2.5em;
padding: .25em;
background-color: rgba(0, 0, 0, .1);
overflow-y: auto;
}
.owl-calendar-wrapper {
padding: 16px !important;
}
.owl-calendar-control {
.owl-calendar-controlNav {
display: flex;
align-items: center;
justify-content: center;
.nav-prev,
.nav-next {
display: flex;
&:before {
font-family: "Material Icons";
position: relative !important;
right: 0 !important;
top: 0 !important;
transform: none !important;
font-size: 20px !important;
width: 20px !important;
height: 20px !important;
min-width: 20px !important;
min-height: 20px !important;
line-height: 20px !important;
content: "chevron_left";
color: rgba(0, 0, 0, 0.54);
}
}
.nav-next:before {
content: "chevron_right";
}
}
.owl-calendar-controlContent {
.month-control,
.year-control {
font-size: 14px;
font-weight: 500;
cursor: pointer;
}
.month-control {
margin-right: 8px;
}
.year-control {
}
}
}
.owl-calendar {
table {
border-spacing: 0 !important;
}
tbody td {
&.owl-calendar-selected {
background-color: $blue;
color: $white;
}
&.owl-calendar-invalid {
color: #ACACAC;
}
&.owl-calendar-outFocus {
color: $grey;
}
&.owl-calendar-hidden {
visibility: hidden;
}
&:not(.owl-calendar-selected):not(.owl-calendar-invalid):hover {
background-color: lighten($blue, 50%);
color: $black;
}
}
}
.owl-years,
.owl-months {
td.owl-year,
td.owl-month {
padding: 0;
font-size: 16px;
width: 72px;
height: 48px;
line-height: 48px;
cursor: pointer;
}
}
.owl-calendar-yearArrow {
width: 24px !important;
height: 24px !important;
&.left {
left: -16px !important;
}
&.right {
right: -16px !important;
}
}
.owl-weekdays {
th.owl-weekday {
height: 32px;
line-height: 32px;
text-align: center;
font-size: 12px;
padding: 0;
color: rgba(0, 0, 0, 0.37);
}
}
.owl-days {
td.owl-day {
height: 32px;
width: 32px;
line-height: 32px;
cursor: pointer;
border-radius: 100%;
padding: 0;
&.owl-day-today:before {
content: '';
display: block;
position: absolute;
right: 2px;
top: 2px;
border-top: .5em solid lighten($blue, 20%);
border-left: .5em solid transparent;
}
}
}
.owl-timer-wrapper {
height: 88px;
padding: 8px !important;
background-color: rgba(0, 0, 0, 0.06);
.owl-timer-input {
background: none;
width: 100% !important;
text-align: center;
}
.owl-timer-text {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 40%;
font-size: 20px;
}
.owl-meridian-btn {
font-size: .8em;
color: $blue;
background-image: none;
background-color: transparent;
border-color: $blue;
&:hover {
color: $white;
background-color: $blue;
border-color: $blue;
}
}
}
.owl-timer-divider {
display: inline-block;
position: absolute;
width: 8px;
height: 100%;
left: -2px;
.owl-timer-dot {
display: block;
background: rgba(0, 0, 0, 0.37);
width: 3px;
height: 3px;
position: absolute;
left: 50%;
border-radius: 100%;
transform: translateX(-50%);
&.dot-top {
top: 40%;
}
&.dot-bottom {
bottom: 40%;
}
}
}
}

View File

@@ -22,9 +22,10 @@ export class FuseConfigService
// Set the default settings
this.defaultSettings = {
layout : {
navigation: 'left', // 'right', 'left', 'top', none
toolbar : 'below', // 'above', 'below', none
footer : 'none' // 'above', 'below', none
navigation: 'left', // 'right', 'left', 'top', 'none'
toolbar : 'below', // 'above', 'below', 'none'
footer : 'below', // 'above', 'below', 'none'
mode : 'fullwidth' // 'boxed', 'fullwidth'
},
colorClasses : {
toolbar: 'md-white-500-bg',

View File

@@ -12,6 +12,7 @@ import { SearchFakeDb } from './search';
import { QuickPanelFakeDb } from './quick-panel';
import { IconsFakeDb } from './icons';
import { ProjectsDashboardDb } from './projects-dashboard';
import { ScrumboardFakeDb } from './scrumboard';
export class FuseFakeDbService implements InMemoryDbService
{
@@ -42,7 +43,8 @@ export class FuseFakeDbService implements InMemoryDbService
'quick-panel-events' : QuickPanelFakeDb.events,
'icons' : IconsFakeDb.icons,
'projects-dashboard-projects': ProjectsDashboardDb.projects,
'projects-dashboard-widgets' : ProjectsDashboardDb.widgets
'projects-dashboard-widgets' : ProjectsDashboardDb.widgets,
'scrumboard-boards' : ScrumboardFakeDb.boards
};
}
}

View File

@@ -0,0 +1,787 @@
export class ScrumboardFakeDb
{
public static boards = [
{
'id' : '32gfhaf2',
'name' : 'ACME Frontend Application',
'uri' : 'acme-frontend-application',
'settings': {
'color' : 'fuse-dark',
'subscribed' : false,
'cardCoverImages': true
},
'lists' : [
{
'id' : '56027cf5a2ca3839a5d36103',
'name' : 'Design',
'idCards': [
'5603a2a3cab0c8300f6096b3',
'44d1.2b51ea6cc2b5d.21f4a3412e857.8ffa2d8b44ad9.ac87215ed53a1.67d4921ad8f8d.9f318bcb2'
]
},
{
'id' : '56127cf2a2ca3539g7d36103',
'name' : 'Development',
'idCards': [
'2837273da9b93dd84243s0f9',
'5787b7e4740c57bf0dffd5b6',
'5637273da9b93bb84743a0f9',
'7987.9740ba532b0d4.f9d12243f7362.507c0738dc561.87fba0a03df6e.75e6508cacf10.7a9835b54'
]
},
{
'id' : 'faf244627326f1249525763d',
'name' : 'Upcoming Features',
'idCards': [
'd9005a4b89ed2aadca48a6ad',
'f6b9d7a9247e5d794a081927',
'80ed.24ad3b18e2668.f28fbbceeeff9.5a834620a42f1.5909be19a2bf2.6c4a54947ce2d.da356b0c1',
'0ad2.7862f947bc456.f42b446df54cb.d1dd9e93601a1.9deb1406d1404.0b3c278fc7001.733341b42',
'bad3.51be8ad33acaf.9540ecb37f7e8.6bee596cfe7d3.44c68bee289c4.b96ed0b9f0af7.e14846035'
]
},
{
'id' : 'ad7d.9fffac5dff412.c83bca6853767.8fd7549b2b1ca.ceda8a01774c4.a5cf3976e87e4.ce79eeeea',
'name' : 'Known Bugs',
'idCards': [
'acc6.9c673cd2f5e35.521e91d8d5991.4b2a95e0539d1.027930c0743c5.7ad1ea7bea476.e8fbe6347',
'3279.3d69b40cc0b75.690252b6bea08.1e1789b0b7c2e.2f264b8661ce2.84d5f56910e23.429be5e8a',
'ba01.8e1a43f92a03a.0022bd5cbb9ba.275c64d911d8c.880e0846a3966.f75ff43e53ad.48ad612e7'
]
}
],
'cards' : [
{
'id' : '2837273da9b93dd84243s0f9',
'name' : 'Update generators',
'description' : 'Current generator doesn\'t support Node.js 6 and above.',
'idAttachmentCover': '',
'idMembers' : [
'26027s1930450d8bf7b10828'
],
'idLabels' : [
'26022e4129ad3a5sc28b36cd'
],
'attachments' : [],
'subscribed' : false,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'AngularCLI could be a nice alternative.',
'time' : 'now'
}
],
'activities' : [],
'due' : null
},
{
'id' : '5603a2a3cab0c8300f6096b3',
'name' : 'Change background colors',
'description' : '',
'idAttachmentCover': '67027cahbe3b52ecf2dc631c',
'idMembers' : [
'76027g1930450d8bf7b10958'
],
'idLabels' : [
'56027e4119ad3a5dc28b36cd',
'5640635e19ad3a5dc21416b2'
],
'attachments' : [
{
'id' : '67027cahbe3b52ecf2dc631c',
'name': 'mail.jpg',
'src' : 'assets/images/scrumboard/mail.jpg',
'time': 'Added Nov 3 at 15:22AM',
'type': 'image'
},
{
'id' : '56027cfcbe1b72ecf1fc452a',
'name': 'calendar.jpg',
'src' : 'assets/images/scrumboard/calendar.jpg',
'time': 'Added Nov 1 at 12:34PM',
'type': 'image'
}
],
'subscribed' : true,
'checklists' : [
{
'id' : '63021cfdbe1x72wcf1fc451v',
'name' : 'Checklist',
'checkItemsChecked': 1,
'checkItems' : [
{
'name' : 'Implement a calendar library',
'checked': false
},
{
'name' : 'Replace event colors with Material Design colors',
'checked': true
},
{
'name' : 'Replace icons with Material Design icons',
'checked': false
},
{
'name' : 'Use moment.js',
'checked': false
}
]
},
{
'name' : 'Checklist 2',
'id' : '74031cfdbe1x72wcz1dc166z',
'checkItemsChecked': 1,
'checkItems' : [
{
'name' : 'Replace event colors with Material Design colors',
'checked': true
},
{
'name' : 'Replace icons with Material Design icons',
'checked': false
},
{
'name' : 'Use moment.js',
'checked': false
}
]
}
],
'checkItems' : 7,
'checkItemsChecked': 2,
'comments' : [
{
'idMember': '56027c1930450d8bf7b10758',
'message' : 'We should be able to add moment.js without any problems',
'time' : '12 mins. ago'
},
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'I added a link for a page that might help us deciding the colors',
'time' : '30 mins. ago'
}
],
'activities' : [
{
'idMember': '56027c1930450d8bf7b10758',
'message' : 'added a comment',
'time' : '12 mins. ago'
},
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'added a comment',
'time' : '30 mins. ago'
},
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'attached a link',
'time' : '45 mins. ago'
}
],
'due' : 'Tue Aug 29 2017 13:16:34 GMT+0300 (Turkey Standard Time)'
},
{
'id' : '5637273da9b93bb84743a0f9',
'name' : 'Fix splash screen bugs',
'description' : '',
'idAttachmentCover': '',
'idMembers' : [
'56027c1930450d8bf7b10758'
],
'idLabels' : [
'5640635e19ad3a5dc21416b2'
],
'attachments' : [],
'subscribed' : true,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [],
'activities' : [],
'due' : null
},
{
'id' : 'd9005a4b89ed2aadca48a6ad',
'name' : 'Add alternative authentication pages',
'description' : '',
'idAttachmentCover': '',
'idMembers' : [
'36027j1930450d8bf7b10158'
],
'idLabels' : [
'6540635g19ad3s5dc31412b2',
'56027e4119ad3a5dc28b36cd'
],
'attachments' : [],
'subscribed' : false,
'checklists' : [
{
'id' : 'dbfb.99bd0ad37dabc.e05046f0c824d.18f26bb524c96.78bebc8488634.240c0ee6a5e45.4cb872965',
'name' : 'Pages',
'checkItemsChecked': 2,
'checkItems' : [
{
'name' : 'Login',
'checked': true
},
{
'name' : 'Register',
'checked': true
},
{
'name' : 'Lost Password',
'checked': false
},
{
'name' : 'Recover Password',
'checked': false
},
{
'name' : 'Activate Account',
'checked': false
}
]
}
],
'checkItems' : 5,
'checkItemsChecked': 2,
'comments' : [],
'activities' : [],
'due' : null
},
{
'id' : '5787b7e4740c57bf0dffd5b6',
'name' : 'Fix the console',
'description' : 'We need to fix the console asap!',
'idAttachmentCover': '',
'idMembers' : [],
'idLabels' : [
'26022e4129ad3a5sc28b36cd'
],
'attachments' : [],
'subscribed' : true,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'I\'m on it!',
'time' : 'now'
}
],
'activities' : [],
'due' : 'Fri Sep 07 2018 15:00:00 GMT+0300 (Turkey Standard Time)'
},
{
'id' : 'f6b9d7a9247e5d794a081927',
'name' : 'New media player',
'description' : '',
'idAttachmentCover': '',
'idMembers' : [
'76027g1930450d8bf7b10958',
'56027c1930450d8bf7b10758',
'26027s1930450d8bf7b10828'
],
'idLabels' : [
'5640635e19ad3a5dc21416b2',
'6540635g19ad3s5dc31412b2'
],
'attachments' : [],
'subscribed' : false,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [],
'activities' : [],
'due' : null
},
{
'id' : 'acc6.9c673cd2f5e35.521e91d8d5991.4b2a95e0539d1.027930c0743c5.7ad1ea7bea476.e8fbe6347',
'name' : 'Memory Leak',
'description' : '',
'idAttachmentCover': '',
'idMembers' : [
'36027j1930450d8bf7b10158'
],
'idLabels' : [
'26022e4129ad3a5sc28b36cd',
'5640635e19ad3a5dc21416b2'
],
'attachments' : [],
'subscribed' : false,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [],
'activities' : [],
'due' : null
},
{
'id' : '3279.3d69b40cc0b75.690252b6bea08.1e1789b0b7c2e.2f264b8661ce2.84d5f56910e23.429be5e8a',
'name' : 'Broken toolbar on profile page',
'description' : '',
'idAttachmentCover': '',
'idMembers' : [
'26027s1930450d8bf7b10828'
],
'idLabels' : [
'26022e4129ad3a5sc28b36cd'
],
'attachments' : [],
'subscribed' : false,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'This should be a medium priority bug, shouldn\'t it?',
'time' : 'now'
}
],
'activities' : [],
'due' : null
},
{
'id' : 'ba01.8e1a43f92a03a.0022bd5cbb9ba.275c64d911d8c.880e0846a3966.f75ff43e53ad.48ad612e7',
'name' : 'Button hover style',
'description' : 'If there are 3 or more buttons in certain page, weird flashing happens when you hover over the red ones.',
'idAttachmentCover': '',
'idMembers' : [
'26027s1930450d8bf7b10828'
],
'idLabels' : [
'26022e4129ad3a5sc28b36cd',
'5640635e19ad3a5dc21416b2'
],
'attachments' : [],
'subscribed' : true,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [],
'activities' : [],
'due' : 'Wed Mar 08 2017 12:00:00 GMT+0300 (Turkey Standard Time)'
},
{
'id' : '80ed.24ad3b18e2668.f28fbbceeeff9.5a834620a42f1.5909be19a2bf2.6c4a54947ce2d.da356b0c1',
'name' : 'New header designs',
'description' : '',
'idAttachmentCover': '12027cafbe3b52ecf2ef632c',
'idMembers' : [],
'idLabels' : [
'56027e4119ad3a5dc28b36cd',
'6540635g19ad3s5dc31412b2',
'5640635e19ad3a5dc21416b2'
],
'attachments' : [
{
'id' : '12027cafbe3b52ecf2ef632c',
'name': 'header-.jpg',
'src' : 'assets/images/scrumboard/header-1.jpg',
'time': 'Added Nov 3 at 15:22AM',
'type': 'image'
},
{
'id' : '55027ced1e1a12ecf1fced2a',
'name': 'header-2.jpg',
'src' : 'assets/images/scrumboard/header-2.jpg',
'time': 'Added Nov 1 at 12:34PM',
'type': 'image'
}
],
'subscribed' : false,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'Currently we have two new designs ready to ship.',
'time' : 'now'
}
],
'activities' : [],
'due' : null
},
{
'id' : '0ad2.7862f947bc456.f42b446df54cb.d1dd9e93601a1.9deb1406d1404.0b3c278fc7001.733341b42',
'name' : 'Fixed footer',
'description' : '',
'idAttachmentCover': '',
'idMembers' : [
'26027s1930450d8bf7b10828',
'56027c1930450d8bf7b10758'
],
'idLabels' : [
'6540635g19ad3s5dc31412b2'
],
'attachments' : [],
'subscribed' : true,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [],
'activities' : [],
'due' : null
},
{
'id' : 'bad3.51be8ad33acaf.9540ecb37f7e8.6bee596cfe7d3.44c68bee289c4.b96ed0b9f0af7.e14846035',
'name' : 'Collapsable navigation',
'description' : '',
'idAttachmentCover': '',
'idMembers' : [],
'idLabels' : [
'6540635g19ad3s5dc31412b2'
],
'attachments' : [],
'subscribed' : false,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'I\'m not sure why we re-doing the navigation. The current collapsable navigation works flawlessly.',
'time' : 'now'
}
],
'activities' : [],
'due' : null
},
{
'id' : '44d1.2b51ea6cc2b5d.21f4a3412e857.8ffa2d8b44ad9.ac87215ed53a1.67d4921ad8f8d.9f318bcb2',
'name' : 'Mail app new layout',
'description' : 'Current layout has lots of flaws in mobile. Outlook view should help with that.',
'idAttachmentCover': '',
'idMembers' : [
'56027c1930450d8bf7b10758',
'26027s1930450d8bf7b10828',
'76027g1930450d8bf7b10958',
'36027j1930450d8bf7b10158'
],
'idLabels' : [
'56027e4119ad3a5dc28b36cd',
'26022e4129ad3a5sc28b36cd'
],
'attachments' : [],
'subscribed' : false,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [],
'activities' : [],
'due' : null
},
{
'id' : '7987.9740ba532b0d4.f9d12243f7362.507c0738dc561.87fba0a03df6e.75e6508cacf10.7a9835b54',
'name' : 'API recover and monitoring',
'description' : 'We need a service to monitor and recover failed APIs.',
'idAttachmentCover': '',
'idMembers' : [
'36027j1930450d8bf7b10158',
'76027g1930450d8bf7b10958'
],
'idLabels' : [
'26022e4129ad3a5sc28b36cd',
'5640635e19ad3a5dc21416b2'
],
'attachments' : [],
'subscribed' : true,
'checklists' : [
{
'id' : '6926.2b31d119e4a.889401e0ca7a0.13ad8ce2e569d.976e54e8b5d87.456afccd7e820.d6c77106a',
'name' : 'API Monitoring',
'checkItemsChecked': 2,
'checkItems' : [
{
'name' : 'Simple dashboard design',
'checked': false
},
{
'name' : 'Should be able to see different time periods on the same dashboard',
'checked': true
},
{
'name' : 'Different colors for different clusters',
'checked': true
}
]
},
{
'id' : '7c22.5261c7924387f.248e8b1d32205.003f7a9f501d1.1d48dcdbe8b23.8099dcc5f75a7.29a966196',
'name' : 'API Recovery',
'checkItemsChecked': 1,
'checkItems' : [
{
'name' : 'Warning notifications to all developers',
'checked': false
},
{
'name' : 'Immediate recovery options attached to the notifications',
'checked': true
},
{
'name' : 'Backups every 6hours',
'checked': false
}
]
}
],
'checkItems' : 6,
'checkItemsChecked': 3,
'comments' : [],
'activities' : [],
'due' : 'Fri Feb 02 2017 14:20:34 GMT+0300 (Turkey Standard Time)'
}
],
'members' : [
{
'id' : '56027c1930450d8bf7b10758',
'name' : 'Alice Freeman',
'avatar': 'assets/images/avatars/alice.jpg'
},
{
'id' : '26027s1930450d8bf7b10828',
'name' : 'Danielle Obrien',
'avatar': 'assets/images/avatars/danielle.jpg'
},
{
'id' : '76027g1930450d8bf7b10958',
'name' : 'James Lewis',
'avatar': 'assets/images/avatars/james.jpg'
},
{
'id' : '36027j1930450d8bf7b10158',
'name' : 'Vincent Munoz',
'avatar': 'assets/images/avatars/vincent.jpg'
}
],
'labels' : [
{
'id' : '26022e4129ad3a5sc28b36cd',
'name' : 'High Priority',
'color': 'md-red-500-bg'
},
{
'id' : '56027e4119ad3a5dc28b36cd',
'name' : 'Design',
'color': 'md-orange-400-bg'
},
{
'id' : '5640635e19ad3a5dc21416b2',
'name' : 'App',
'color': 'md-blue-600-bg'
},
{
'id' : '6540635g19ad3s5dc31412b2',
'name' : 'Feature',
'color': 'md-green-400-bg'
}
]
},
{
'id' : '27cfcbe1',
'name' : 'ACME Backend Application',
'uri' : 'acme-backend-application',
'settings': {
'color' : 'blue-grey',
'subscribed' : false,
'cardCoverImages': true
},
'lists' : [
{
'id' : '56027cf5a2ca3839a5d36103',
'name' : 'Designs',
'idCards': [
'5603a2a3cab0c8300f6096b3'
]
},
{
'id' : '56127cf2a2ca3539g7d36103',
'name' : 'Development',
'idCards': [
'5637273da9b93bb84743a0f9'
]
}
],
'cards' : [
{
'id' : '5603a2a3cab0c8300f6096b3',
'name' : 'Calendar App Design',
'description' : '',
'idAttachmentCover': '56027cfcbe1b72ecf1fc452a',
'idMembers' : [
'56027c1930450d8bf7b10758',
'36027j1930450d8bf7b10158'
],
'idLabels' : [
'56027e4119ad3a5dc28b36cd',
'5640635e19ad3a5dc21416b2'
],
'attachments' : [
{
'id' : '56027cfcbe1b72ecf1fc452a',
'name': 'calendar-app-design.jpg',
'src' : 'assets/images/scrumboard/calendar.jpg',
'time': 'Added Nov 1 at 12:34PM',
'type': 'image'
},
{
'id' : '67027cahbe3b52ecf2dc631c',
'url' : 'assets/images/scrumboard/calendar.jpg',
'time': 'Added Nov 3 at 15:22AM',
'type': 'link'
}
],
'subscribed' : true,
'checklists' : [
{
'id' : '63021cfdbe1x72wcf1fc451v',
'name' : 'Checklist',
'checkItemsChecked': 1,
'checkItems' : [
{
'name' : 'Implement a calendar library',
'checked': false
},
{
'name' : 'Replace event colors with Material Design colors',
'checked': true
},
{
'name' : 'Replace icons with Material Design icons',
'checked': false
},
{
'name' : 'Use moment.js',
'checked': false
}
]
},
{
'name' : 'Checklist 2',
'id' : '74031cfdbe1x72wcz1dc166z',
'checkItemsChecked': 1,
'checkItems' : [
{
'name' : 'Replace event colors with Material Design colors',
'checked': true
},
{
'name' : 'Replace icons with Material Design icons',
'checked': false
},
{
'name' : 'Use moment.js',
'checked': false
}
]
}
],
'checkItems' : 7,
'checkItemsChecked': 2,
'comments' : [
{
'idMember': '56027c1930450d8bf7b10758',
'message' : 'We should be able to add moment.js without any problems',
'time' : '12 mins. ago'
},
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'I added a link for a page that might help us deciding the colors',
'time' : '30 mins. ago'
}
],
'activities' : [
{
'idMember': '56027c1930450d8bf7b10758',
'message' : 'added a comment',
'time' : '12 mins. ago'
},
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'added a comment',
'time' : '30 mins. ago'
},
{
'idMember': '36027j1930450d8bf7b10158',
'message' : 'attached a link',
'time' : '45 mins. ago'
}
],
'due' : null
},
{
'id' : '5637273da9b93bb84743a0f9',
'name' : 'Fix Splash Screen bugs',
'description' : '',
'idAttachmentCover': '5603a2ae2bbd55bb2db57478',
'idMembers' : [
'56027c1930450d8bf7b10758'
],
'idLabels' : [],
'attachments' : [
{
'id' : '5603a2ae2bbd55bb2db57478',
'name': 'mail-app-design.jpg',
'src' : 'assets/images/scrumboard/mail.jpg',
'time': 'Added Nov 1 at 12:34PM',
'type': 'image'
}
],
'subscribed' : true,
'checklists' : [],
'checkItems' : 0,
'checkItemsChecked': 0,
'comments' : [],
'activities' : [],
'due' : null
}
],
'members' : [
{
'id' : '56027c1930450d8bf7b10758',
'name' : 'Alice Freeman',
'avatar': 'assets/images/avatars/alice.jpg'
},
{
'id' : '26027s1930450d8bf7b10828',
'name' : 'Danielle Obrien',
'avatar': 'assets/images/avatars/danielle.jpg'
},
{
'id' : '76027g1930450d8bf7b10958',
'name' : 'James Lewis',
'avatar': 'assets/images/avatars/james.jpg'
},
{
'id' : '36027j1930450d8bf7b10158',
'name' : 'Vincent Munoz',
'avatar': 'assets/images/avatars/vincent.jpg'
}
],
'labels' : [
{
'id' : '56027e4119ad3a5dc28b36cd',
'name' : 'Design',
'color': 'md-red-500-bg'
},
{
'id' : '5640635e19ad3a5dc21416b2',
'name' : 'App',
'color': 'md-blue-500-bg'
},
{
'id' : '6540635g19ad3s5dc31412b2',
'name' : 'Feature',
'color': 'md-green-400-bg'
}
]
}
];
}

View File

@@ -3,6 +3,7 @@
flex: 1 0 auto;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 20%, rgba(255, 255, 255, 0.8));
overflow: hidden;
max-width: 100%;
.chat {

View File

@@ -1,4 +1,4 @@
<div id="calendar" class="page-layout carded fullwidth">
<div id="chat" class="page-layout carded fullwidth">
<!-- TOP BACKGROUND -->
<div class="top-bg md-accent-bg"></div>

View File

@@ -1,4 +1,6 @@
#calendar {
@import "src/app/core/scss/fuse";
#chat {
display: flex;
flex: 1;
@@ -7,6 +9,10 @@
max-width: 1400px;
margin: 0 auto;
@include media-breakpoint-down(md) {
padding: 0 !important;
}
.content-card {
display: flex;
flex: 1;
@@ -27,6 +33,10 @@
height: auto;
}
> .mat-drawer-content {
max-width: 100%;
}
md-sidenav {
display: flex;
flex-direction: column;

View File

@@ -11,6 +11,10 @@
.toolbar-bottom {
height: 240px;
@include media-breakpoint-down(md) {
height: 180px;
}
}
}

View File

@@ -11,6 +11,10 @@
.toolbar-bottom {
height: 240px;
@include media-breakpoint-down(md) {
height: 180px;
}
}
}

View File

@@ -688,7 +688,7 @@
<!-- / CENTER -->
<!-- SIDENAV -->
<md-sidenav class="sidenav mat-sidenav-opened" align="end" opened="true" mode="side" fuseMdSidenavHelper="dashboards-right-sidenav" md-is-locked-open="gt-md">
<md-sidenav class="sidenav mat-sidenav-opened" align="end" mode="side" fuseMdSidenavHelper="dashboards-right-sidenav" md-is-locked-open="gt-md">
<div class="sidenav-content" perfect-scrollbar>
<!-- WIDGET GROUP -->

View File

@@ -10,6 +10,7 @@
.mat-row {
position: relative;
cursor: pointer;
min-height: 64px;
.mat-cell {

View File

@@ -52,7 +52,8 @@
<md-input-container>
<textarea mdInput name="message"
placeholder="Message"
formControlName="message">
formControlName="message"
rows="6">
</textarea>
</md-input-container>

View File

@@ -8,23 +8,30 @@
padding: 0;
width: 720px;
.attachment-list {
font-size: 13px;
padding-top: 16px;
.compose-form {
.mat-input-container {
width: 100%;
}
.attachment {
background-color: rgba(0, 0, 0, 0.08);
border: 1px solid rgba(0, 0, 0, 0.16);
padding-left: 16px;
margin-top: 8px;
border-radius: 2px;
.attachment-list {
font-size: 13px;
padding-top: 16px;
.filename {
font-weight: 500;
}
.attachment {
background-color: rgba(0, 0, 0, 0.08);
border: 1px solid rgba(0, 0, 0, 0.16);
padding-left: 16px;
margin-top: 8px;
border-radius: 2px;
&:last-child {
margin-bottom: 0;
.filename {
font-weight: 500;
}
&:last-child {
margin-bottom: 0;
}
}
}
}

View File

@@ -0,0 +1,84 @@
import { FuseUtils } from '../../../../core/fuseUtils';
import { List } from './list.model';
import { Card } from './card.model';
const sampleLabels = [
{
'id' : '56027e4119ad3a5dc28b36cd',
'name' : 'Design',
'color': 'md-red-500-bg'
},
{
'id' : '5640635e19ad3a5dc21416b2',
'name' : 'App',
'color': 'md-blue-500-bg'
},
{
'id' : '6540635g19ad3s5dc31412b2',
'name' : 'Feature',
'color': 'md-green-400-bg'
}
];
const sampleMembers = [
{
id : '56027c1930450d8bf7b10758',
name : 'Alice Freeman',
avatar: 'assets/images/avatars/alice.jpg'
},
{
id : '26027s1930450d8bf7b10828',
name : 'Danielle Obrien',
avatar: 'assets/images/avatars/danielle.jpg'
},
{
id : '76027g1930450d8bf7b10958',
name : 'James Lewis',
avatar: 'assets/images/avatars/james.jpg'
},
{
id : '36027j1930450d8bf7b10158',
name : 'Vincent Munoz',
avatar: 'assets/images/avatars/vincent.jpg'
}
];
export class Board
{
name: string;
uri: string;
id: string;
settings: {
color: string,
subscribed: boolean,
cardCoverImages: boolean
};
lists: List[];
cards: Card[];
members: {
id: string,
name: string,
avatar: string
}[];
labels: {
id: string,
name: string,
color: string
}[];
constructor(board)
{
this.name = board.name || 'Untitled Board';
this.uri = board.uri || 'untitled-board';
this.id = board.id || FuseUtils.generateGUID();
this.settings = board.settings || {
color : '',
subscribed : true,
cardCoverImages: true
};
this.lists = [];
this.cards = [];
this.members = board.members || sampleMembers;
this.labels = board.labels || sampleLabels;
}
}

View File

@@ -0,0 +1,23 @@
<div class="list new-list mat-elevation-z1">
<button *ngIf="!formActive" md-button class="new-list-form-button" (click)="openForm()">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon class="md-red-bg">add</md-icon>
<span>Add a list</span>
</div>
</button>
<form *ngIf="formActive" class="new-list-form" [formGroup]="form" (submit)="onFormSubmit()"
fxFlex="1 0 auto" fxFlex="row" fxLayoutAlign="start center">
<input formControlName="name" #nameInput fxFlex placeholder="Write a list Name">
<button md-icon-button fxFlex="0 1 auto">
<md-icon>check</md-icon>
</button>
<button md-icon-button fxFlex="0 1 auto" (click)="closeForm()">
<md-icon>close</md-icon>
</button>
</form>
</div>

View File

@@ -0,0 +1,32 @@
:host {
.new-list {
border-radius: 2px;
background-color: #EEF0F2;
.new-list-form-button {
text-transform: none;
font-size: 15px;
padding: 0 16px;
height: 64px;
margin: 0;
width: 100%;
md-icon {
border-radius: 50%;
height: 40px;
width: 40px;
line-height: 40px;
margin-right: 16px;
}
}
.new-list-form {
padding: 16px;
height: 64px;
> input {
height: 100%;
}
}
}
}

View File

@@ -0,0 +1,57 @@
import { Component, EventEmitter, OnInit, Output, ViewChild} from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector : 'fuse-scrumboard-board-add-list',
templateUrl: './add-list.component.html',
styleUrls : ['./add-list.component.scss']
})
export class FuseScrumboardBoardAddListComponent implements OnInit
{
formActive = false;
form: FormGroup;
@Output() onlistAdd = new EventEmitter();
@ViewChild('nameInput') nameInputField;
constructor(
private formBuilder: FormBuilder
)
{
}
ngOnInit()
{
}
openForm()
{
this.form = this.formBuilder.group({
name: ['']
});
this.formActive = true;
this.focusNameField();
}
closeForm()
{
this.formActive = false;
}
focusNameField()
{
setTimeout(() => {
this.nameInputField.nativeElement.focus();
});
}
onFormSubmit()
{
if ( this.form.valid )
{
this.onlistAdd.next(this.form.getRawValue().name);
this.formActive = false;
}
}
}

View File

@@ -0,0 +1,83 @@
<md-sidenav-container>
<div id="board">
<!-- HEADER -->
<div class="header md-accent-bg p-16 p-md-24" [class]="'md-'+board.settings.color+'-bg'" fxLayout="column">
<div class="header-content" fxLayout="row" fxLayoutAlign="space-between" fxFlex="1 0 auto" fxLayoutWrap>
<!-- BOARD SELECTION BUTTON -->
<div fxLayout="row" fxLayoutAlign="center center" fxFlexOrder="2" fxFlexOrder.gt-xs="1">
<button md-raised-button class="mat-accent header-boards-button"
[class]="'md-'+board.settings.color+'-700-bg'"
routerLink="/apps/scrumboard/boards"
aria-label="boards button">
<md-icon>assessment</md-icon>
<span>BOARDS</span>
</button>
</div>
<!-- / BOARD SELECTION BUTTON -->
<!-- BOARD NAME -->
<div class="header-board-name mb-8 mb-md-0"
fxLayout="row" fxLayoutAlign="center center"
fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="center center"
fxFlex="1 0 100%" fxFlex.gt-xs="1 0 auto"
fxFlexOrder="1" fxFlexOrder.gt-xs="2">
<md-icon *ngIf="board.settings.subscribed" class="board-subscribe s-16">remove_red_eye</md-icon>
<fuse-scrumboard-edit-board-name
[board]="board"
(onNameChanged)="onBoardNameChanged($event)">
</fuse-scrumboard-edit-board-name>
</div>
<!-- / BOARD NAME -->
<!-- TOOLBAR -->
<div class="toolbar" fxLayout="row" fxLayoutAlign="space-between center" fxFlexOrder="3">
<!-- BOARD SETTINGS BUTTON -->
<button md-icon-button (click)="settingsSidenav.toggle()"
aria-label="Settings" md-tooltip="Settings">
<md-icon>settings</md-icon>
</button>
<!-- / BOARD SETTINGS BUTTON -->
</div>
<!-- / TOOLBAR -->
</div>
</div>
<!-- / HEADER -->
<div fxFlex class="board-content-wrapper p-16 p-md-24">
<!-- BOARD -->
<div class="board-content ngx-dnd-container p-16 p-md-24" [class]="board.settings.color+'-100-bg'" fxLayout="row"
ngxDroppable="list" [model]="board.lists" (out)="onDrop($event)">
<!-- LIST -->
<fuse-scrumboard-board-list
class="scrumboard-board-list list-wrapper ngx-dnd-item"
ngxDraggable
*ngFor="let list of board.lists"
[model]="list"
[list]="list">
</fuse-scrumboard-board-list>
<!-- / LIST -->
<!-- NEW LIST BUTTON-->
<fuse-scrumboard-board-add-list class="new-list-wrapper" (onlistAdd)="onListAdd($event)"></fuse-scrumboard-board-add-list>
<!-- / NEW LIST BUTTON-->
</div>
<!-- / BOARD -->
</div>
<!-- primary content -->
</div>
<md-sidenav #settingsSidenav align="end">
<fuse-scrumboard-board-settings></fuse-scrumboard-board-settings>
</md-sidenav>
</md-sidenav-container>

View File

@@ -0,0 +1,132 @@
@import "src/app/core/scss/fuse";
:host {
md-sidenav-container {
width: 100%;
height: 100%;
md-sidenav {
width: 320px !important;
min-width: 320px !important;
max-width: 320px !important;
}
#board {
flex-direction: column;
display: flex;
height: 100%;
> .header {
position: relative;
min-height: 96px;
background-image: none;
z-index: 49;
.header-content {
.header-boards-button {
margin: 0;
}
.header-board-name {
font-size: 16px;
.board-subscribe {
margin-right: 8px;
}
.editable-buttons {
md-icon {
color: #FFFFFF !important;
}
}
}
.right-side {
> .md-button:last-child {
margin-right: 0;
}
}
}
}
#board-selector {
position: absolute;
top: 96px;
right: 0;
left: 0;
height: 192px;
z-index: 48;
padding: 24px;
opacity: 1;
.board-list-item {
width: 128px;
height: 192px;
padding: 16px;
cursor: pointer;
position: relative;
.board-name {
text-align: center;
padding: 16px 0;
}
.selected-icon {
position: absolute;
top: 0;
left: 50%;
width: 32px;
height: 32px;
margin-left: -16px;
border-radius: 50%;
text-align: center;
color: white;
i {
line-height: 32px !important;
}
}
&.add-new-board {
opacity: 0.6;
}
}
}
.board-content-wrapper {
position: relative;
.board-content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: 100%;
background: #E5E7E8;
overflow-y: hidden;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
.list-sortable-placeholder {
background: rgba(0, 0, 0, 0.06);
margin-right: 24px;
}
.new-list-wrapper {
width: 344px;
min-width: 344px;
max-width: 344px;
padding-right: 24px;
}
}
}
}
}
}

View File

@@ -0,0 +1,60 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ScrumboardService } from '../scrumboard.service';
import { Subscription } from 'rxjs/Subscription';
import { Location } from '@angular/common';
import { List } from '../list.model';
@Component({
selector : 'fuse-scrumboard-board',
templateUrl: './board.component.html',
styleUrls : ['./board.component.scss']
})
export class FuseScrumboardBoardComponent implements OnInit, OnDestroy
{
board: any;
onBoardChanged: Subscription;
constructor(
private route: ActivatedRoute,
private location: Location,
private scrumboardService: ScrumboardService
)
{
}
ngOnInit()
{
this.onBoardChanged =
this.scrumboardService.onBoardChanged
.subscribe(board => {
this.board = board;
});
}
onListAdd(newListName)
{
if ( newListName === '' )
{
return;
}
this.scrumboardService.addList(new List({name: newListName}));
}
onBoardNameChanged(newName)
{
this.scrumboardService.updateBoard();
this.location.go('/apps/scrumboard/boards/' + this.board.id + '/' + this.board.uri);
}
onDrop(ev)
{
this.scrumboardService.updateBoard();
}
ngOnDestroy()
{
this.onBoardChanged.unsubscribe();
}
}

View File

@@ -0,0 +1,452 @@
<md-toolbar *ngIf="card" md-dialog-title class="md-accent-bg m-0">
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<div fxFlex fxLayout="row" fxLayoutAlign="start center">
<!-- DUE DATE -->
<div class="due-date" fxFlex="0 1 auto">
<button *ngIf="card.due" md-icon-button class="" [mdMenuTriggerFor]="dueDateMenu">
<md-icon>today</md-icon>
</button>
<md-menu #dueDateMenu="mdMenu" [overlapTrigger]="false">
<button md-menu-item (click)="removeDueDate()">Remove Due Date</button>
</md-menu>
<md2-datepicker *ngIf="!card.due" [(ngModel)]="card.due" openOnFocus="true" placeholder="Set Date/Time" type="datetime"></md2-datepicker>
</div>
<!-- / DUE DATE -->
<!-- LABELS -->
<div class="labels" fxFlex="0 1 auto">
<button md-icon-button [mdMenuTriggerFor]="labelsMenu">
<md-icon>label</md-icon>
</button>
<md-menu #labelsMenu="mdMenu" [overlapTrigger]="false" class="scrumboard-labels-menu">
<fuse-scrumboard-label-selector [card]="card" (onCardLabelsChange)="updateCard()"></fuse-scrumboard-label-selector>
</md-menu>
</div>
<!-- / LABELS -->
<!-- MEMBERS -->
<div class="members" fxFlex="0 1 auto">
<button md-icon-button class="" [mdMenuTriggerFor]="membersMenu">
<md-icon>account_circle</md-icon>
</button>
<md-menu #membersMenu="mdMenu" [overlapTrigger]="false">
<div fxFlex fxLayout="column" class="scrumboard-members-menu" (click)="$event.stopPropagation()">
<md-checkbox class="member px-12" [checked]="card.idMembers.indexOf(member.id) > -1"
*ngFor="let member of board.members"
(change)="toggleInArray(member.id, card.idMembers);updateCard()">
<div fxLayout="row" fxLayoutAlign="start center">
<img [alt]="member.name" [src]=" member.avatar" class="avatar"/>
<p class="member-name">{{ member.name }}</p>
</div>
</md-checkbox>
</div>
</md-menu>
</div>
<!-- / MEMBERS -->
<!-- ATTACHMENT -->
<button md-icon-button aria-label="Attachment">
<md-icon>attachment</md-icon>
</button>
<!-- / ATTACHMENT -->
<!-- CHECKLIST -->
<div class="due-date " fxFlex="0 1 auto">
<button md-icon-button class="" [mdMenuTriggerFor]="checklistMenu" #checklistMenuTrigger="mdMenuTrigger" (onMenuOpen)="onChecklistMenuOpen()">
<md-icon>check_box</md-icon>
</button>
<md-menu #checklistMenu="mdMenu" [overlapTrigger]="false">
<form class="px-16 py-8" #newChecklistForm="ngForm" (submit)="addChecklist(newChecklistForm)" (click)="$event.stopPropagation()"
fxLayout="column" fxLayoutAlign="start end">
<md-input-container floatPlaceholder="never" (click)="$event.stopPropagation()" fxFlex>
<input #newCheckListTitleField mdInput ngModel #checklistTitle="ngModel" name="checklistTitle" placeholder="Checklist title" required>
</md-input-container>
<button md-raised-button class="mat-accent" aria-label="Add Checklist" [disabled]="!newChecklistForm.valid">Add Checklist</button>
</form>
</md-menu>
</div>
<!-- / CHECKLIST -->
<!-- SUBSCRIBE -->
<div class="subscribe " fxFlex="0 1 auto">
<button md-icon-button class="" [mdMenuTriggerFor]="subscribeMenu">
<md-icon>remove_red_eye</md-icon>
</button>
<md-menu #subscribeMenu="mdMenu" [overlapTrigger]="false">
<button *ngIf="card.subscribed" md-menu-item (click)="toggleSubscribe()">
Unsubscribe
</button>
<button *ngIf="!card.subscribed" md-menu-item (click)="toggleSubscribe()">
Subscribe
</button>
</md-menu>
</div>
<!-- / SUBSCRIBE -->
<!-- OPTIONS -->
<div class="options " fxFlex="0 1 auto">
<button md-icon-button class="" [mdMenuTriggerFor]="optionsMenu">
<md-icon>more_horiz</md-icon>
</button>
<md-menu #optionsMenu="mdMenu" [overlapTrigger]="false">
<button md-menu-item (click)="removeCard()">
Remove Card
</button>
</md-menu>
</div>
<!-- / OPTIONS -->
</div>
<!-- CLOSE DIALOG BUTTON -->
<button md-icon-button (click)="dialogRef.close()" aria-label="Close Dialog">
<md-icon>close</md-icon>
</button>
<!-- / CLOSE DIALOG BUTTON -->
</div>
</md-toolbar>
<div *ngIf="card" md-dialog-content class="p-24 m-0" perfect-scrollbar>
<div fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="space-between center"
fxLayout.xs="column" fxLayoutAlign="center center">
<!-- BREADCRUMB -->
<div class="card-breadcrumb mb-16 mb-sm-0" fxLayout="row" fxLayoutAlign="start center">
<span>{{board.name}}</span>
<md-icon class="s-20">chevron_right</md-icon>
<span>{{list.name}}</span>
</div>
<!-- / BREADCRUMB -->
<!-- DUE DATE -->
<div *ngIf="card.due" class="due-date" fxLayout="row" fxLayoutAlign="start center">
<md2-datepicker class="picker ml-8" [(ngModel)]="card.due" openOnFocus="true" type="datetime" format="dd/MM/y H:mm"></md2-datepicker>
<button md-icon-button class="remove-due-date" (click)="removeDueDate()">
<md-icon class="s-16">close</md-icon>
</button>
</div>
<!-- / DUE DATE -->
</div>
<!-- NAME -->
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon *ngIf="card.subscribed" class="card-subscribe s-20 mr-12">remove_red_eye</md-icon>
<div class="card-name">
<md-input-container floatPlaceholder="never" fxFlex>
<input mdInput [(ngModel)]="card.name" placeholder="Title" required (change)="updateCard()">
</md-input-container>
</div>
</div>
<!-- / NAME -->
<!-- DESCRIPTION -->
<div class="description">
<md-input-container fxFlex>
<textarea mdInput [(ngModel)]="card.description" placeholder="Description" columns="1" md-maxlength="150" max-rows="4" (change)="updateCard()"></textarea>
</md-input-container>
</div>
<!-- / DESCRIPTION -->
<!-- SECTIONS -->
<div class="sections">
<!-- LABELS SECTION -->
<div *ngIf="card.idLabels[0] || card.idMembers[0]" class="section"
fxLayout="column" fxLayout.gt-xs="row">
<div *ngIf="card.idLabels[0]" fxFlex class="labels">
<div class="section-header" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-20">label</md-icon>
<span class="section-title">Labels</span>
</div>
<div class="section-content">
<md-chip-list class="label-chips">
<md-chip class="label-chip mb-4"
*ngFor="let labelId of card.idLabels"
[class]="board.labels | getById:labelId:'color'"
fxLayout="row" fxLayoutAlign="start center">
<span>{{board.labels|getById:labelId:'name'}}</span>
<md-icon class="ml-8 s-16 chip-remove" (click)="toggleInArray(labelId, card.idLabels);updateCard()">close</md-icon>
</md-chip>
</md-chip-list>
</div>
</div>
<div *ngIf="card.idMembers[0]" fxFlex class="members">
<div class="section-header" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-20">supervisor_account</md-icon>
<span class="section-title">Members</span>
</div>
<div class="section-content">
<md-chip-list class="member-chips">
<md-chip class="member-chip mb-4" *ngFor="let memberId of card.idMembers"
fxLayout="row" fxLayoutAlign="start center">
<img class="member-chip-avatar" [src]="board.members | getById:memberId:'avatar'"
[md-tooltip]="board.members | getById:memberId:'name'">
<md-icon class="ml-8 s-16 chip-remove" (click)="toggleInArray(memberId, card.idMembers);updateCard()">close</md-icon>
</md-chip>
</md-chip-list>
</div>
</div>
</div>
<!-- / LABELS SECTION -->
<!-- ATTACHMENTS SECTION -->
<div *ngIf="card.attachments[0]" class="section">
<div class="attachments">
<div class="section-header" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-20">attachment</md-icon>
<span class="section-title">Attachments</span>
</div>
<div class="section-content">
<div class="attachment" *ngFor="let item of card.attachments" [ngSwitch]="item.type">
<div *ngSwitchCase="'image'"
fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center"
fxLayout="column" fxLayoutAlign="center start">
<div class="attachment-preview mat-elevation-z2"
[ngStyle]="{'background-image': 'url('+item.src+')'}">
</div>
<div class="attachment-content" fxLayout="column">
<div fxLayout="row" fxLayoutAlign="start center">
<span class="attachment-name">{{item.name}}</span>
<md-icon *ngIf="card.idAttachmentCover === item.id"
class="yellow-700-fg attachment-is-cover s-20">star
</md-icon>
</div>
<span class="attachment-time">{{item.time}}</span>
<div>
<button md-raised-button class="attachment-actions-button" [mdMenuTriggerFor]="attachmentActionsMenu">
<span fxLayout="row" fxLayoutAlign="center center">
<span>Actions</span>
<md-icon class="s-20">arrow_drop_down</md-icon>
</span>
</button>
<md-menu #attachmentActionsMenu="mdMenu">
<button md-menu-item (click)="toggleCoverImage(item.id)">
<span *ngIf="card.idAttachmentCover !== item.id">Make Cover</span>
<span *ngIf="card.idAttachmentCover === item.id">Remove Cover</span>
</button>
<button md-menu-item (click)="removeAttachment(item)">
Remove Attachment
</button>
</md-menu>
</div>
</div>
</div>
<div *ngSwitchCase="'link'" fxLayout="row">
<div class="attachment-preview mat-elevation-z2" fxLayout="column"
fxLayoutAlign="center center">
<span>LINK</span>
</div>
<div class="attachment-content" fxLayout="column">
<span class="attachment-url">{{item.url}}</span>
<span class="attachment-time">{{item.time}}</span>
</div>
</div>
</div>
<button md-button class="add-attachment-button" aria-label="add attachment">
<div fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-20">add</md-icon>
<span>Add an attachment</span>
</div>
</button>
</div>
</div>
</div>
<!-- / ATTACHMENTS SECTION -->
<!-- CHECKLISTS SECTION -->
<div class="section" *ngFor="let checklist of card.checklists">
<div class="checklist">
<div class="section-header" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-20">check_box</md-icon>
<span fxFlex class="section-title">{{checklist.name}}</span>
<div>
<button md-icon-button class="checklist-actions-button" [mdMenuTriggerFor]="checklistActionsMenu">
<md-icon class="s-20">more_vert</md-icon>
</button>
<md-menu #checklistActionsMenu="mdMenu">
<button md-menu-item (click)="removeChecklist(checklist)">
<md-icon>delete</md-icon>
<span>Remove Checklist</span>
</button>
</md-menu>
</div>
</div>
<div class="section-content">
<div class="checklist-progress" fxLayout="row" fxLayoutAlign="start center">
<span class="checklist-progress-value">
{{checklist.checkItemsChecked}} / {{checklist.checkItems.length}}
</span>
<md-progress-bar class="mat-accent checklist-progressbar" mode="determinate"
value="{{100 * checklist.checkItemsChecked / checklist.checkItems.length}}">
</md-progress-bar>
</div>
<div class="check-items">
<div class="check-item" *ngFor="let checkItem of checklist.checkItems" fxLayout="row" fxLayoutAlign="space-between center">
<div fxFlex fxLayout="row" fxLayoutAlign="start center">
<md-checkbox [(ngModel)]="checkItem.checked"
(change)="updateCheckedCount(checklist)"
aria-label="{{checkItem.name}}">
</md-checkbox>
<md-input-container fxFlex class="mx-12">
<input mdInput [(ngModel)]="checkItem.name">
</md-input-container>
</div>
<button md-icon-button class="checklist-actions-button" (click)="removeChecklistItem(checkItem, checklist)">
<md-icon class="s-20">delete</md-icon>
</button>
</div>
</div>
<form #newCheckItemForm="ngForm" (submit)="addCheckItem(newCheckItemForm,checklist)" name="newCheckItemForm" class="new-check-item-form"
fxLayout="row" fxLayoutAlign="start center">
<div fxLayout="row" fxLayoutAlign="start center" fxFlex>
<md-icon class="s-20">add</md-icon>
<md-input-container class="no-errors-spacer mx-12" fxFlex>
<input mdInput ngModel #checkItem="ngModel" name="checkItem" placeholder="Add an item" autocomplete="off">
</md-input-container>
</div>
<button md-raised-button
[disabled]="!newCheckItemForm.valid || newCheckItemForm.pristine"
class="mat-accent" aria-label="Add">
<span>Add</span>
</button>
</form>
</div>
</div>
</div>
<!-- / CHECKLISTS SECTION -->
<!-- COMMENTS SECTION -->
<div class="section">
<div class="comments">
<div class="section-header" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-20">comment</md-icon>
<span class="section-title">Comments</span>
</div>
<div class="section-content">
<form name="cardCommentForm"
#newCommentForm="ngForm" (submit)="addNewComment(newCommentForm)"
ng-submit="vm.addNewComment(vm.newCommentText); vm.newCommentText =''"
class="comment new-comment" fxLayout="column" fxLayoutAlign="start" no-validate>
<div fxLayout="row">
<img class="comment-member-avatar" src="assets/images/avatars/profile.jpg">
<md-input-container fxFlex>
<input mdInput name="newComment" ngModel #newComment="ngModel"
placeholder="Write a comment.." required>
</md-input-container>
</div>
<div fxLayout="row" fxLayoutAlign="end center">
<button md-raised-button class="mat-accent"
[disabled]="!newCommentForm.valid || newCommentForm.pristine"
aria-label="Add">
<span>Add</span>
</button>
</div>
</form>
<div class="comment" fxLayout="row" *ngFor="let comment of card.comments">
<img class="comment-member-avatar"
[src]="board.members | getById: comment.idMember:'avatar'">
<div fxLayout="column">
<div class="comment-member-name">
{{board.members | getById: comment.idMember:'name'}}
</div>
<div class="comment-bubble">{{comment.message}}</div>
<div class="comment-time secondary-text">{{comment.time}}</div>
</div>
</div>
</div>
</div>
</div>
<!-- / COMMENTS SECTION -->
<!-- ACTIVITIES SECTION -->
<div *ngIf="card.activities[0]" class="section">
<div class="activities">
<div class="section-header" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-20">list</md-icon>
<span class="section-title">Activity</span>
</div>
<div class="section-content">
<div class="activity" fxLayout="row" fxLayoutAlign="start center" *ngFor="let activity of card.activities">
<img class="activity-member-avatar"
[src]="board.members| getById:activity.idMember:'avatar'">
<div class="activity-member-name">{{board.members| getById:activity.idMember:'name'}}</div>
<div class="activity-message">{{activity.message}}</div>
<div class="activity-time secondary-text">{{activity.time}}</div>
</div>
</div>
</div>
</div>
<!-- / ACTIVITIES SECTION -->
</div>
<!-- / SECTIONS -->
</div>

View File

@@ -0,0 +1,437 @@
@import "src/app/core/scss/fuse";
:host {
display: flex;
flex-direction: column;
}
.scrumboard-card-dialog {
.mat-dialog-container {
padding: 0;
width: 720px;
.mat-toolbar {
.due-date {
md2-datepicker {
min-width: initial;
.md2-datepicker-trigger {
padding: 0;
.md2-datepicker-button {
display: block;
position: relative;
top: 0;
left: 0;
line-height: normal;
}
.md2-datepicker-input {
display: none;
}
}
}
}
}
.mat-dialog-content {
position: relative;
background-color: #F5F5F5;
.card-breadcrumb {
font-weight: 500;
font-size: 14px;
}
.card-subscribe {
margin-right: 8px;
color: rgba(0, 0, 0, 0.6);
}
.picker {
width: 140px;
min-width: 140px;
}
.card-name {
width: 100%;
font-size: 22px;
@include media-breakpoint(xs) {
font-size: 14px;
}
}
.due-date {
md2-datepicker {
width: 180px;
min-width: 180px;
.md2-datepicker-trigger {
padding-top: 5px;
padding-bottom: 5px;
.md2-datepicker-button {
top: 0;
}
.md2-datepicker-input {
min-width: initial;
}
}
}
.remove-due-date {
}
}
.description {
padding-bottom: 16px;
}
.sections {
.section {
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
margin-bottom: 32px;
&:last-child {
border-bottom: none;
margin-bottom: 0;
.section-content {
padding-bottom: 0;
}
}
.section-header {
font-size: 16px;
md-icon {
margin-right: 8px;
color: rgba(0, 0, 0, 0.6);
}
.section-title {
font-weight: 500;
}
}
.section-content {
padding: 24px 0 32px 0;
}
.labels {
.section-content {
padding: 8px 0 32px 0;
}
.label-chips {
box-shadow: none;
padding: 0;
.label-chip {
display: block;
.chip-remove {
cursor: pointer;
}
}
}
}
.members {
.section-content {
padding: 8px 0 32px 0;
}
.member-chips {
box-shadow: none;
padding: 0;
.member-chip {
padding: 4px 12px 4px 4px;
.member-chip-avatar {
width: 32px;
border-radius: 50%;
}
.chip-remove {
cursor: pointer;
}
}
}
}
.attachments {
.attachment {
margin-bottom: 16px;
.attachment-preview {
background-color: #EEF0F2;
width: 160px;
height: 128px;
background-size: contain;
background-position: 50% 50%;
background-repeat: no-repeat;
margin-right: 24px;
font-weight: 500;
color: rgba(0, 0, 0, 0.6);
}
.attachment-content {
.attachment-url,
.attachment-name {
font-weight: 500;
font-size: 16px;
}
.attachment-is-cover {
margin-left: 6px;
}
.attachment-time {
color: rgba(0, 0, 0, 0.6);
}
.attachment-actions-button {
background-color: white;
text-transform: capitalize;
margin: 12px 0 0 0;
padding-left: 12px;
md-icon {
margin-left: 8px;
color: rgba(0, 0, 0, 0.6);
}
}
}
}
.add-attachment-button {
margin: 0;
md-icon {
color: rgba(0, 0, 0, 0.6);
margin-right: 8px;
}
span {
font-weight: 500;
text-transform: capitalize;
}
}
}
.checklist {
.checklist-progress {
margin-bottom: 16px;
.checklist-progress-value {
margin-right: 12px;
font-weight: 500;
white-space: nowrap;
font-size: 14px;
}
.checklist-progressbar {
}
}
.editable-wrap {
flex: 1
}
.check-items {
.check-item {
md-checkbox {
margin-bottom: 0;
.md-label {
font-size: 14px;
}
&.md-checked {
.md-label {
text-decoration: line-through;
color: rgba(0, 0, 0, 0.6);
}
}
}
}
}
.new-check-item-form {
padding-top: 16px;
md-input-container {
margin: 0;
}
.md-button {
margin: 0 0 0 16px;
}
}
}
.comments {
.comment {
margin-bottom: 16px;
.comment-member-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
margin-right: 16px;
}
.comment-member-name {
font-size: 14px;
font-weight: 500;
}
.comment-time {
font-size: 12px;
}
.comment-bubble {
position: relative;
padding: 8px;
background-color: white;
border: 1px solid rgb(220, 223, 225);
font-size: 14px;
margin: 4px 0;
&:after,
&:before {
content: ' ';
position: absolute;
width: 0;
height: 0;
}
&:after {
left: -7px;
right: auto;
top: 0px;
bottom: auto;
border: 11px solid;
border-color: white transparent transparent transparent;
}
&:before {
left: -9px;
right: auto;
top: -1px;
bottom: auto;
border: 8px solid;
border-color: rgb(220, 223, 225) transparent transparent transparent;
}
}
&.new-comment {
md-input-container {
margin: 0;
}
}
}
}
.activities {
.activity {
margin-bottom: 12px;
.activity-member-avatar {
width: 24px;
height: 24px;
border-radius: 50%;
margin-right: 16px;
}
.activity-member-name {
font-size: 14px;
font-weight: 500;
margin-right: 8px;
}
.activity-message {
font-size: 14px;
margin-right: 8px;
}
.activity-time {
font-size: 12px;
}
}
}
}
}
}
}
}
.scrumboard-members-menu {
width: 240px;
.mat-checkbox-layout,
.mat-checkbox-label {
display: flex;
flex: 1;
}
}
.scrumboard-labels-menu {
.mat-menu-content {
padding-bottom: 0;
.mat-checkbox-layout,
.mat-checkbox-label {
display: flex;
flex: 1;
}
.views {
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
width: 240px;
min-width: 240px;
max-width: 240px;
min-height: 240px;
.view {
position: absolute;
width: 240px;
height: 100%;
bottom: 0;
left: 0;
right: 0;
top: 0;
> .header {
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
}
}
}
}

View File

@@ -0,0 +1,277 @@
import { Component, Inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { MD_DIALOG_DATA, MdDialog, MdDialogRef, MdMenuTrigger } from '@angular/material';
import { Subscription } from 'rxjs/Subscription';
import { ScrumboardService } from '../../../scrumboard.service';
import { NgForm } from '@angular/forms/src/forms';
import { FuseUtils } from '../../../../../../../core/fuseUtils';
import { FuseConfirmDialogComponent } from '../../../../../../../core/components/confirm-dialog/confirm-dialog.component';
@Component({
selector : 'fuse-scrumboard-board-card-dialog',
templateUrl : './card.component.html',
styleUrls : ['./card.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseScrumboardCardDialogComponent implements OnInit, OnDestroy
{
card: any;
board: any;
list: any;
onBoardChanged: Subscription;
toggleInArray = FuseUtils.toggleInArray;
@ViewChild('checklistMenuTrigger') checklistMenu: MdMenuTrigger;
@ViewChild('newCheckListTitleField') newCheckListTitleField;
confirmDialogRef: MdDialogRef<FuseConfirmDialogComponent>;
constructor(
public dialogRef: MdDialogRef<FuseScrumboardCardDialogComponent>,
@Inject(MD_DIALOG_DATA) private data: any,
public dialog: MdDialog,
private scrumboardService: ScrumboardService
)
{
}
ngOnInit()
{
this.onBoardChanged =
this.scrumboardService.onBoardChanged
.subscribe(board => {
this.board = board;
this.card = this.board.cards.find((_card) => {
return this.data.cardId === _card.id;
});
this.list = this.board.lists.find((_list) => {
return this.data.listId === _list.id;
});
});
}
/**
* Remove Due date
*/
removeDueDate()
{
this.card.due = '';
this.updateCard();
}
/**
* Toggle Subscribe
*/
toggleSubscribe()
{
this.card.subscribed = !this.card.subscribed;
this.updateCard();
}
/**
* Toggle Cover Image
* @param attachmentId
*/
toggleCoverImage(attachmentId)
{
if ( this.card.idAttachmentCover === attachmentId )
{
this.card.idAttachmentCover = '';
}
else
{
this.card.idAttachmentCover = attachmentId;
}
this.updateCard();
}
/**
* Remove Attachment
* @param attachment
*/
removeAttachment(attachment)
{
if ( attachment.id === this.card.idAttachmentCover )
{
this.card.idAttachmentCover = '';
}
this.card.attachments.splice(this.card.attachments.indexOf(attachment), 1);
this.updateCard();
}
/**
* Remove Checklist
* @param checklist
*/
removeChecklist(checklist)
{
this.card.checklists.splice(this.card.checklists.indexOf(checklist), 1);
this.updateCard();
}
/**
* Update Checked Count
* @param list
*/
updateCheckedCount(list)
{
const checkItems = list.checkItems;
let checkedItems = 0;
let allCheckedItems = 0;
let allCheckItems = 0;
for ( const checkItem of checkItems )
{
if ( checkItem.checked )
{
checkedItems++;
}
}
list.checkItemsChecked = checkedItems;
for ( const item of this.card.checklists )
{
allCheckItems += item.checkItems.length;
allCheckedItems += item.checkItemsChecked;
}
this.card.checkItems = allCheckItems;
this.card.checkItemsChecked = allCheckedItems;
this.updateCard();
}
/**
* Remove Checklist Item
* @param checkItem
* @param checklist
*/
removeChecklistItem(checkItem, checklist)
{
checklist.checkItems.splice(checklist.checkItems.indexOf(checkItem), 1);
this.updateCheckedCount(checklist);
this.updateCard();
}
/**
* Add Check Item
* @param {NgForm} form
* @param checkList
*/
addCheckItem(form: NgForm, checkList)
{
const checkItemVal = form.value.checkItem;
if ( !checkItemVal || checkItemVal === '' )
{
return;
}
const newCheckItem = {
'name' : checkItemVal,
'checked': false
};
checkList.checkItems.push(newCheckItem);
this.updateCheckedCount(checkList);
form.setValue({checkItem: ''});
this.updateCard();
}
/**
* Add Checklist
* @param {NgForm} form
*/
addChecklist(form: NgForm)
{
this.card.checklists.push({
id : FuseUtils.generateGUID(),
name : form.value.checklistTitle,
checkItemsChecked: 0,
checkItems : []
});
form.setValue({checklistTitle: ''});
form.resetForm();
this.checklistMenu.closeMenu();
this.updateCard();
}
/**
* On Checklist Menu Open
*/
onChecklistMenuOpen()
{
setTimeout(() => {
this.newCheckListTitleField.nativeElement.focus();
});
}
/**
* Add New Comment
* @param {NgForm} form
*/
addNewComment(form: NgForm)
{
const newCommentText = form.value.newComment;
const newComment = {
idMember: '36027j1930450d8bf7b10158',
message : newCommentText,
time : 'now'
};
this.card.comments.unshift(newComment);
form.setValue({newComment: ''});
this.updateCard();
}
/**
* Remove Card
*/
removeCard()
{
this.confirmDialogRef = this.dialog.open(FuseConfirmDialogComponent, {
disableClose: false
});
this.confirmDialogRef.componentInstance.confirmMessage = 'Are you sure you want to delete the card?';
this.confirmDialogRef.afterClosed().subscribe(result => {
if ( result )
{
this.dialogRef.close();
this.scrumboardService.removeCard(this.card.id, this.list.id);
}
});
}
/**
* Update Card
*/
updateCard()
{
this.scrumboardService.updateCard(this.card);
}
ngOnDestroy()
{
this.onBoardChanged.unsubscribe();
}
}

View File

@@ -0,0 +1,71 @@
<div [ngSwitch]="labelsMenuView" class="views" (click)="$event.stopPropagation()">
<div class="view " *ngSwitchCase="'labels'" [@slideInLeft] fxFlex fxLayout="column">
<div class="header mb-12 pb-4 px-8" fxLayout="row" fxLayoutAlign="space-between center">
<div>Labels</div>
<button md-button (click)="labelsMenuView ='add'">
<md-icon class="s-16">add</md-icon>
<span>Add</span>
</button>
</div>
<div fxFlex fxLayout="column" perfect-scrollbar>
<div class="label pl-12 mx-8 mb-8" *ngFor="let label of board.labels" fxFlex="0 0 auto" fxLayout="row" fxFlexAlign="space-between center"
[class]="label.color">
<md-checkbox fxFlex fxLayout="row" fxLayoutAlign="start center" [checked]="card.idLabels.indexOf(label.id) > -1"
(change)="toggleInArray(label.id, card.idLabels);cardLabelsChanged()">
{{label.name}}
</md-checkbox>
<button md-icon-button>
<md-icon class="s-16" (click)="labelsMenuView ='edit';selectedLabel = label">mode_edit</md-icon>
</button>
</div>
</div>
</div>
<div class="view px-8" *ngSwitchCase="'edit'" [@slideInRight] fxFlex fxLayout="column">
<div class="header mb-12 pb-4" fxLayout="row" fxLayoutAlign="space-between center">
<div>Edit Label</div>
<button md-icon-button (click)="labelsMenuView ='labels'">
<md-icon class="s-16">arrow_back</md-icon>
</button>
</div>
<div fxLayout="row" fxLayoutAlign="space-between center">
<md-input-container>
<input mdInput placeholder="Name" [(ngModel)]="selectedLabel.name" (change)="onLabelChange()">
</md-input-container>
<fuse-material-color-picker [(selectedClass)]="selectedLabel.color" class="ml-8" (change)="$event.preventDefault;onLabelChange()"></fuse-material-color-picker>
</div>
</div>
<div class="view px-8" *ngSwitchCase="'add'" [@slideInRight] fxFlex fxLayout="column">
<div class="header mb-12 pb-4" fxLayout="row" fxLayoutAlign="space-between center">
<div>Add Label</div>
<button md-icon-button (click)="labelsMenuView ='labels'">
<md-icon class="s-16">arrow_back</md-icon>
</button>
</div>
<form (submit)="addNewLabel()" #newLabelForm="ngForm" fxFlex fxLayout="column" fxLayoutAlign="start end">
<div class="w-100-p" fxFlex="0 1 auto" fxLayout="row" fxLayoutAlign="space-between center">
<md-input-container fxFlex>
<input mdInput placeholder="Name" [(ngModel)]="newLabel.name" name="labelName" required>
</md-input-container>
<fuse-material-color-picker [(selectedClass)]="newLabel.color" name="labelColor" class="ml-8"></fuse-material-color-picker>
</div>
<button md-raised-button class="mat-accent mt-16" [disabled]="!newLabelForm.valid">Add</button>
</form>
</div>
</div>

View File

@@ -0,0 +1,37 @@
.scrumboard-labels-menu {
.mat-menu-content {
padding-bottom: 0;
.mat-checkbox-layout,
.mat-checkbox-label {
display: flex;
flex: 1;
}
.views {
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
width: 240px;
min-width: 240px;
max-width: 240px;
min-height: 240px;
.view {
position: absolute;
width: 240px;
height: 100%;
bottom: 0;
left: 0;
right: 0;
top: 0;
> .header {
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
}
}
}
}

View File

@@ -0,0 +1,70 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { ScrumboardService } from '../../../../scrumboard.service';
import { FuseUtils } from '../../../../../../../../core/fuseUtils';
import { Animations } from '../../../../../../../../core/animations';
@Component({
selector : 'fuse-scrumboard-label-selector',
templateUrl : './label-selector.component.html',
styleUrls : ['./label-selector.component.scss'],
encapsulation: ViewEncapsulation.None,
animations : [Animations.slideInLeft, Animations.slideInRight]
})
export class FuseScrumboardLabelSelectorComponent implements OnInit, OnDestroy
{
board: any;
@Input('card') card: any;
@Output() onCardLabelsChange = new EventEmitter();
labelsMenuView = 'labels';
selectedLabel: any;
newLabel = {
'id' : '',
'name' : '',
'color': 'md-blue-400-bg'
};
toggleInArray = FuseUtils.toggleInArray;
onBoardChanged: Subscription;
constructor(
private scrumboardService: ScrumboardService
)
{
}
ngOnInit()
{
this.onBoardChanged =
this.scrumboardService.onBoardChanged
.subscribe(board => {
this.board = board;
});
}
cardLabelsChanged()
{
this.onCardLabelsChange.next();
}
onLabelChange()
{
this.scrumboardService.updateBoard();
}
addNewLabel()
{
this.newLabel.id = FuseUtils.generateGUID();
this.board.labels.push(Object.assign({}, this.newLabel));
this.newLabel.name = '';
this.labelsMenuView = 'labels';
}
ngOnDestroy()
{
this.onBoardChanged.unsubscribe();
}
}

View File

@@ -0,0 +1,21 @@
<div *ngIf="!formActive" class="board-name" fxFlex="1 0 auto" fxLayout="row" fxLayoutAlign="start center">
<span>{{board.name}}</span>
<button md-icon-button (click)="openForm()">
<md-icon class="s-16">edit_mode</md-icon>
</button>
</div>
<form [formGroup]="form" (ngSubmit)="onFormSubmit()"
class="board-name-form" fxFlex="1 0 auto"
*ngIf="formActive" fxFlex="row">
<input formControlName="name" #nameInput fxFlex="1 0 auto" placeholder="Write a board name">
<button md-icon-button fxFlex="0 1 auto">
<md-icon>check</md-icon>
</button>
<button md-icon-button fxFlex="0 1 auto" (click)="closeForm()" type="button">
<md-icon>close</md-icon>
</button>
</form>

View File

@@ -0,0 +1,10 @@
:host {
display: block !important;
.board-name {
text-overflow: ellipsis;
overflow: hidden;
font-size: 15px;
font-weight: 500;
}
}

View File

@@ -0,0 +1,61 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector : 'fuse-scrumboard-edit-board-name',
templateUrl: './edit-board-name.component.html',
styleUrls : ['./edit-board-name.component.scss']
})
export class FuseScrumboardEditBoardNameComponent implements OnInit
{
formActive = false;
form: FormGroup;
@Input() board;
@Output() onNameChanged = new EventEmitter();
@ViewChild('nameInput') nameInputField;
constructor(
private formBuilder: FormBuilder
)
{
}
ngOnInit()
{
}
openForm()
{
this.form = this.formBuilder.group({
name: [this.board.name]
});
this.formActive = true;
this.focusNameField();
}
closeForm()
{
this.formActive = false;
}
focusNameField()
{
setTimeout(() => {
this.nameInputField.nativeElement.focus();
});
}
onFormSubmit()
{
if ( this.form.valid )
{
this.board.name = this.form.getRawValue().name;
this.board.uri = encodeURIComponent(this.board.name).replace(/%20/g, '-').toLowerCase();
this.onNameChanged.next(this.board.name);
this.formActive = false;
}
}
}

View File

@@ -0,0 +1,30 @@
<div *ngIf="!formActive" class="add-card-button"
(click)="openForm()"
fxLayout="row" fxLayoutAlign="start center">
<div>
<md-icon class="s-20">add</md-icon>
</div>
<span>Add a card</span>
</div>
<div *ngIf="formActive" class="add-card-form-wrapper">
<form [formGroup]="form" (submit)="onFormSubmit()" class="add-card-form" fxLayout="column">
<md-input-container fxFlex floatPlaceholder="never" >
<input #nameInput mdInput formControlName="name" placeholder="Card title" autocomplete="off" required>
</md-input-container>
<div class="pl-8" fxLayout="row" fxLayoutAlign="space-between center">
<button md-raised-button class="add-button mat-accent" aria-label="add"
[disabled]="form.invalid">
<span>Add</span>
</button>
<button md-icon-button (click)="closeForm()" class="cancel-button" aria-label="cancel">
<md-icon>close</md-icon>
</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,39 @@
:host {
.add-card-button {
position: relative;
height: 48px;
min-height: 48px;
padding: 0 16px;
text-align: left;
text-transform: none;
font-weight: 500;
font-size: 14px;
background-color: #DCDFE2;
cursor: pointer;
border-radius: 2px;
md-icon {
margin-right: 8px;
color: rgba(0, 0, 0, 0.6);
}
}
.add-card-form-wrapper {
background-color: #DCDFE2;
.add-card-form {
z-index: 999;
background: white;
display: block;
position: relative;
padding: 8px;
border-top: 1px solid rgba(0, 0, 0, 0.12);
md-input-container {
width: 100%;
margin: 0;
padding: 12px 8px;
}
}
}
}

View File

@@ -0,0 +1,57 @@
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector : 'fuse-scrumboard-board-add-card',
templateUrl: './add-card.component.html',
styleUrls : ['./add-card.component.scss']
})
export class FuseScrumboardBoardAddCardComponent implements OnInit
{
formActive = false;
form: FormGroup;
@Output() onCardAdd = new EventEmitter();
@ViewChild('nameInput') nameInputField;
constructor(
private formBuilder: FormBuilder
)
{
}
ngOnInit()
{
}
openForm()
{
this.form = this.formBuilder.group({
name: ''
});
this.formActive = true;
this.focusNameField();
}
closeForm()
{
this.formActive = false;
}
focusNameField()
{
setTimeout(() => {
this.nameInputField.nativeElement.focus();
});
}
onFormSubmit()
{
if ( this.form.valid )
{
const cardName = this.form.getRawValue().name;
this.onCardAdd.next(cardName);
this.formActive = false;
}
}
}

View File

@@ -0,0 +1,124 @@
<!-- CARD COVER -->
<div *ngIf="board.settings.cardCoverImages && card.idAttachmentCover"
class="list-card-cover">
<img [src]="card.attachments | getById:card.idAttachmentCover:'src'">
</div>
<!-- / CARD COVER -->
<!-- CARD DETAILS -->
<div class="list-card-details">
<!-- CARD SORT HANDLE -->
<div class="list-card-sort-handle">
<md-icon md-font-icon="icon-cursor-move" class="icon s16"></md-icon>
</div>
<!-- /CARD SORT HANDLE -->
<!-- CARD LABELS -->
<div *ngIf="card.idLabels.length > 0"
class="list-card-labels"
fxLayout="row" layout-wrap>
<span class="list-card-label"
[ngClass]="board.labels | getById:labelId:'color'"
*ngFor="let labelId of card.idLabels"
[md-tooltip]="board.labels | getById:labelId:'name'">
</span>
</div>
<!-- / CARD LABELS -->
<!-- CARD NAME -->
<div class="list-card-name">{{card.name}}</div>
<!-- / CARD NAME -->
<div *ngIf="card.due || card.checkItems"
class="list-card-badges" fxLayout="row" fxLayoutAlign="start center">
<!-- CARD DUE -->
<span class="badge due-date"
[ngClass]="{'overdue': isOverdue(card.due)}"
*ngIf="card.due" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-16">access_time</md-icon>
<span>{{card.due | date:'mediumDate'}}</span>
</span>
<!-- / CARD DUE -->
<!-- CARD CHECK ITEMS STATUS -->
<span *ngIf="card.checkItems"
class="badge check-items"
[ngClass]="{'completed': card.checkItemsChecked === card.checkItems}"
fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-16">check_circle</md-icon>
<span>{{card.checkItemsChecked}}</span>
<span>/</span>
<span>{{card.checkItems}}</span>
</span>
<!-- / CARD CHECK ITEMS STATUS -->
</div>
<!-- CARD MEMBERS -->
<div *ngIf="card.idMembers.length > 0"
class="list-card-members"
fxLayout="row">
<div class="list-card-member"
*ngFor="let memberId of card.idMembers">
<img class="list-card-member-avatar"
[src]="board.members | getById:memberId:'avatar'"
[md-tooltip]="board.members | getById:memberId:'name'">
</div>
</div>
<!-- / CARD MEMBERS -->
</div>
<!-- / CARD DETAILS -->
<!-- CARD FOOTER -->
<div class="list-card-footer" fxLayout="row" fxLayoutAlign="space-between center">
<div fxLayout="row" fxLayoutAlign="start center">
<!-- CARD SUBSCRIBE -->
<span *ngIf="card.subscribed" class="list-card-footer-item"
fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-18">remove_red_eye</md-icon>
</span>
<!-- / CARD SUBSCRIBE -->
<!-- CARD DETAILS -->
<span *ngIf="card.description !== ''"
class="list-card-footer-item" fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-18">description</md-icon>
</span>
<!-- / CARD DETAILS -->
</div>
<div fxLayout="row" fxLayoutAlign="end center">
<!-- CARD ATTACHMENT -->
<span *ngIf="card.attachments"
class="list-card-footer-item"
fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-18">attachment</md-icon>
<span class="value">{{card.attachments.length}}</span>
</span>
<!-- / CARD ATTACHMENT -->
<!-- CARD COMMENTS -->
<span *ngIf="card.comments"
class="list-card-footer-item"
fxLayout="row" fxLayoutAlign="start center">
<md-icon class="s-18">comment</md-icon>
<span class="value">{{card.comments.length}}</span>
</span>
<!-- / CARD COMMENTS -->
</div>
</div>
<!-- CARD FOOTER -->

View File

@@ -0,0 +1,147 @@
@import "src/app/core/scss/fuse";
.scrumboard-board-card {
position: relative;
display: block;
width: 100%;
margin: 16px 0;
background-color: white;
color: #000;
border-radius: 2px;
transition: box-shadow 150ms ease;
cursor: pointer;
.list-card-sort-handle {
display: none;
position: absolute;
top: 0;
right: 0;
padding: 4px;
background: rgba(255, 255, 255, 0.8);
}
.list-card-cover {
}
.list-card-details {
padding: 16px 16px 0 16px;
.list-card-labels {
margin-bottom: 6px;
.list-card-label {
width: 32px;
height: 6px;
border-radius: 6px;
margin: 0 6px 6px 0;
}
}
.list-card-name {
font-size: 14px;
font-weight: 500;
margin-bottom: 12px;
}
.list-card-badges {
margin-bottom: 12px;
.badge {
margin-right: 8px;
padding: 4px 8px;
border-radius: 2px;
background-color: rgba(0, 0, 0, 0.4);
color: #FFFFFF;
md-icon {
margin-right: 4px;
}
&.due-date {
background-color: mat-color(mat-palette($mat-green));;
&.overdue {
background-color: mat-color(mat-palette($mat-red));
}
}
&.check-items {
&.completed {
background-color: mat-color(mat-palette($mat-green));
}
}
}
}
.list-card-members {
margin-bottom: 12px;
.list-card-member {
margin-right: 8px;
.list-card-member-avatar {
border-radius: 50%;
width: 32px;
height: 32px;
}
}
}
}
.list-card-footer {
border-top: 1px solid rgba(0, 0, 0, 0.12);
padding: 0 16px;
.list-card-footer-item {
height: 48px;
margin-right: 12px;
color: rgba(0, 0, 0, 0.66);
.value {
padding-left: 8px;
}
&:last-of-type {
margin-right: 0;
}
}
}
&:not(.has-handle):not(.move-disabled),
&.has-handle [ngxdraghandle],
&.has-handle [ngxDragHandle] {
//cursor: move;
}
.ngx-dnd-content {
user-select: none;
}
&.gu-mirror {
position: fixed !important;
margin: 0 !important;
z-index: 9999 !important;
opacity: 0.8;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
filter: alpha(opacity=80);
@include mat-elevation(7);
}
&.gu-hide {
display: none !important;
}
&.gu-unselectable {
-webkit-user-select: none !important;
-moz-user-select: none !important;
-ms-user-select: none !important;
user-select: none !important;
}
&.gu-transit {
opacity: 0.2;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
filter: alpha(opacity=20);
}
}

View File

@@ -0,0 +1,44 @@
import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ScrumboardService } from '../../../scrumboard.service';
import * as moment from 'moment';
@Component({
selector : 'fuse-scrumboard-board-card',
templateUrl : './card.component.html',
styleUrls : ['./card.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseScrumboardBoardCardComponent implements OnInit
{
@Input() cardId;
card: any;
board: any;
constructor(
private route: ActivatedRoute,
private scrumboardService: ScrumboardService
)
{
}
ngOnInit()
{
this.board = this.route.snapshot.data.board;
this.card = this.board.cards.filter((card) => {
return this.cardId === card.id;
})[0];
}
/**
* Is the card overdue?
*
* @param cardDate
* @returns {boolean}
*/
isOverdue(cardDate)
{
return moment() > moment(new Date(cardDate));
}
}

View File

@@ -0,0 +1,17 @@
<div fxFlex="1 0 auto" *ngIf="!formActive" class="list-header-name" (click)="openForm()">
{{list.name}}
</div>
<form [formGroup]="form" (ngSubmit)="onFormSubmit()"
class="list-header-name-form" fxFlex="1 0 auto"
*ngIf="formActive" fxFlex="row">
<input formControlName="name" #nameInput fxFlex placeholder="Write a list Name">
<button md-icon-button fxFlex="0 1 auto">
<md-icon>check</md-icon>
</button>
<button md-icon-button fxFlex="0 1 auto" (click)="closeForm()" type="button">
<md-icon>close</md-icon>
</button>
</form>

View File

@@ -0,0 +1,9 @@
:host {
.list-header-name {
text-overflow: ellipsis;
overflow: hidden;
font-size: 15px;
font-weight: 500;
cursor: pointer;
}
}

View File

@@ -0,0 +1,59 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector : 'fuse-scrumboard-board-edit-list-name',
templateUrl: './edit-list-name.component.html',
styleUrls : ['./edit-list-name.component.scss']
})
export class FuseScrumboardBoardEditListNameComponent implements OnInit
{
formActive = false;
form: FormGroup;
@Input() list;
@Output() onNameChanged = new EventEmitter();
@ViewChild('nameInput') nameInputField;
constructor(
private formBuilder: FormBuilder
)
{
}
ngOnInit()
{
}
openForm()
{
this.form = this.formBuilder.group({
name: [this.list.name]
});
this.formActive = true;
this.focusNameField();
}
closeForm()
{
this.formActive = false;
}
focusNameField()
{
setTimeout(() => {
this.nameInputField.nativeElement.focus();
});
}
onFormSubmit()
{
if ( this.form.valid )
{
this.list.name = this.form.getRawValue().name;
this.onNameChanged.next(this.list.name);
this.formActive = false;
}
}
}

View File

@@ -0,0 +1,48 @@
<div class="list mat-elevation-z1" fxLayout="column">
<!-- LIST HEADER -->
<div class="list-header" fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<fuse-scrumboard-board-edit-list-name
fxFlex="1 0 auto"
[list]="list"
(onNameChanged)="onListNameChanged($event)">
</fuse-scrumboard-board-edit-list-name>
<div fxFlex="0 1 auto">
<button md-icon-button class="list-header-option-button" [mdMenuTriggerFor]="listMenu">
<md-icon>more_vert</md-icon>
</button>
<md-menu #listMenu="mdMenu">
<button md-menu-item (click)="removeList(list.id)">Remove List</button>
</md-menu>
</div>
</div>
<!-- / LIST HEADER -->
<!-- LIST CONTENT -->
<div class="list-content" fxLayout="column">
<div class="list-cards ngx-dnd-container"
[model]="list.idCards" ngxDroppable="card" (out)="onDrop($event)"
perfect-scrollbar #listScroll>
<fuse-scrumboard-board-card ngxDraggable
(click)="openCardDialog(cardId)"
class="scrumboard-board-card mat-elevation-z2 ngx-dnd-item"
*ngFor="let cardId of list.idCards"
[model]="cardId"
[cardId]="cardId">
</fuse-scrumboard-board-card>
</div>
</div>
<!-- / LIST CONTENT -->
<!-- NEW CARD BUTTON-->
<div class="list-footer">
<fuse-scrumboard-board-add-card (onCardAdd)="onCardAdd($event)">
</fuse-scrumboard-board-add-card>
</div>
<!-- / NEW CARD BUTTON-->
</div>

View File

@@ -0,0 +1,90 @@
@import "src/app/core/scss/fuse";
.scrumboard-board-list {
width: 344px;
min-width: 344px;
max-width: 344px;
padding-right: 24px;
height: 100%;
.list {
max-height: 100%;
background-color: #EEF0F2;
color: #000;
border-radius: 2px;
transition: box-shadow 150ms ease;
.list-header {
height: 64px;
min-height: 64px;
padding: 0 8px 0 16px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
@include media-breakpoint-down(sm) {
height: 48px;
min-height: 48px;
}
}
.list-content {
position: relative;
overflow: hidden;
overflow-y: auto;
min-height: 0;
.list-cards {
position: relative;
min-height: 32px;
padding: 0 16px;
}
}
.list-footer {
display: flex;
flex-direction: column;
flex: 1 0 auto;
min-height: 48px;
}
}
&:not(.has-handle):not(.move-disabled),
&.has-handle [ngxdraghandle],
&.has-handle [ngxDragHandle] {
//cursor: move;
}
.ngx-dnd-content {
user-select: none;
}
&.gu-mirror {
position: fixed !important;
margin: 0 !important;
z-index: 9999 !important;
opacity: 0.8;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
filter: alpha(opacity=80);
> .list {
@include mat-elevation(7);
}
}
&.gu-hide {
display: none !important;
}
&.gu-unselectable {
-webkit-user-select: none !important;
-moz-user-select: none !important;
-ms-user-select: none !important;
user-select: none !important;
}
&.gu-transit {
opacity: 0.2;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
filter: alpha(opacity=20);
}
}

View File

@@ -0,0 +1,108 @@
import { Component, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { FuseUtils } from '../../../../../../core/fuseUtils';
import { ScrumboardService } from 'app/main/content/apps/scrumboard/scrumboard.service';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
import { MdDialog, MdDialogRef } from '@angular/material';
import { FuseScrumboardCardDialogComponent } from '../dialogs/card/card.component';
import { FuseConfirmDialogComponent } from '../../../../../../core/components/confirm-dialog/confirm-dialog.component';
import { Card } from '../../card.model';
@Component({
selector : 'fuse-scrumboard-board-list',
templateUrl : './list.component.html',
styleUrls : ['./list.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseScrumboardBoardListComponent implements OnInit, OnDestroy
{
board: any;
dialogRef: any;
@Input() list;
@ViewChild(PerfectScrollbarDirective) listScroll: PerfectScrollbarDirective;
onBoardChanged: Subscription;
confirmDialogRef: MdDialogRef<FuseConfirmDialogComponent>;
constructor(
private route: ActivatedRoute,
private scrumboardService: ScrumboardService,
public dialog: MdDialog
)
{
}
ngOnInit()
{
this.onBoardChanged =
this.scrumboardService.onBoardChanged
.subscribe(board => {
this.board = board;
});
}
onListNameChanged(newListName)
{
this.list.name = newListName;
}
onCardAdd(newCardName)
{
if ( newCardName === '' )
{
return;
}
this.scrumboardService.addCard(this.list.id, new Card({name: newCardName}));
setTimeout(() => {
this.listScroll.scrollToBottom(0, 400);
});
}
removeList(listId)
{
this.confirmDialogRef = this.dialog.open(FuseConfirmDialogComponent, {
disableClose: false
});
this.confirmDialogRef.componentInstance.confirmMessage = 'Are you sure you want to delete the list and it\'s all cards?';
this.confirmDialogRef.afterClosed().subscribe(result => {
if ( result )
{
this.scrumboardService.removeList(listId);
}
});
}
openCardDialog(cardId)
{
this.dialogRef = this.dialog.open(FuseScrumboardCardDialogComponent, {
panelClass: 'scrumboard-card-dialog',
data : {
cardId: cardId,
listId: this.list.id
}
});
this.dialogRef.afterClosed()
.subscribe(response => {
});
}
onDrop(ev)
{
this.scrumboardService.updateBoard();
}
ngOnDestroy()
{
this.onBoardChanged.unsubscribe();
}
}

View File

@@ -0,0 +1,15 @@
<md-list class="colors">
<!-- COLORS -->
<md-list-item class="color m-8 mat-elevation-z1"
[ngClass]="'md-'+color.key+'-bg'"
*ngFor="let color of (colors | keys)"
(click)="setColor(color.key)"
md-ripple>
<p fxFlex>{{color.key}}</p>
<md-icon class="s-16" *ngIf="color.key === board.settings.color">check</md-icon>
<button md-icon-button *ngIf="color.key === board.settings.color" (click)="$event.stopPropagation();setColor('')">
<md-icon class="s-16">delete</md-icon>
</button>
</md-list-item>
<!-- / COLORS -->
</md-list>

View File

@@ -0,0 +1,10 @@
:host {
.colors {
.color {
position: relative;
cursor: pointer;
}
}
}

View File

@@ -0,0 +1,43 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatColors } from '../../../../../../../../core/matColors';
import { ScrumboardService } from '../../../../scrumboard.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector : 'fuse-scrumboard-board-color-selector',
templateUrl: './board-color-selector.component.html',
styleUrls : ['./board-color-selector.component.scss']
})
export class FuseScrumboardBoardColorSelectorComponent implements OnInit, OnDestroy
{
colors: any;
board: any;
onBoardChanged: Subscription;
constructor(
private scrumboardService: ScrumboardService
)
{
this.colors = MatColors.all;
}
ngOnInit()
{
this.onBoardChanged =
this.scrumboardService.onBoardChanged
.subscribe(board => {
this.board = board;
});
}
setColor(color)
{
this.board.settings.color = color;
this.scrumboardService.updateBoard();
}
ngOnDestroy()
{
this.onBoardChanged.unsubscribe();
}
}

View File

@@ -0,0 +1,76 @@
<div [ngSwitch]="view" class="views" (click)="$event.stopPropagation()">
<div class="view" *ngSwitchCase="'main'" [@slideInLeft]>
<!-- SIDENAV HEADER -->
<div class="header md-accent-bg px-24" [class]="'md-'+board.settings.color+'-bg'" fxLayout="column" fxLayoutAlign="center center">
<div>Settings</div>
</div>
<!-- / SIDENAV HEADER -->
<!-- SIDENAV CONTENT -->
<div class="content py-16" perfect-scrollbar>
<div class="nav">
<div class="nav-item">
<div class="nav-link" md-ripple (click)="view = 'board-color'">
<md-icon class="nav-link-icon">format_color_fill</md-icon>
<p class="title">Board Color</p>
</div>
</div>
<div class="nav-item">
<div class="nav-link" md-ripple (click)="toggleCardCover()">
<md-icon class="nav-link-icon">photo</md-icon>
<p fxFlex class="title">Card Cover Images</p>
<md-icon *ngIf="board.settings.cardCoverImages" class="s-18">check</md-icon>
</div>
</div>
<div class="nav-item">
<div class="nav-link" md-ripple (click)="toggleSubcription()">
<md-icon class="nav-link-icon">remove_red_eye</md-icon>
<p fxFlex class="title">Subscribe</p>
<md-icon *ngIf="board.settings.subscribed" class="s-18">check</md-icon>
</div>
</div>
<div class="nav-item">
<div class="nav-link" md-ripple>
<md-icon class="nav-link-icon">content_copy</md-icon>
<p class="title">Copy Board</p>
</div>
</div>
<div class="nav-item">
<div class="nav-link" md-ripple>
<md-icon class="nav-link-icon">delete</md-icon>
<p class="title">Delete Board</p>
</div>
</div>
<md-divider></md-divider>
</div>
</div>
<!-- / SIDENAV CONTENT -->
</div>
<div class="view" *ngSwitchCase="'board-color'" [@slideInRight]>
<!-- SIDENAV HEADER -->
<div class="header md-accent-bg px-24" [class]="'md-'+board.settings.color+'-bg'" fxLayout="row" fxLayoutAlign="space-between center">
<div>Background Color</div>
<button md-icon-button (click)="view ='main'">
<md-icon class="s-16">arrow_back</md-icon>
</button>
</div>
<!-- / SIDENAV HEADER -->
<!-- SIDENAV CONTENT -->
<div class="content p-8" perfect-scrollbar>
<fuse-scrumboard-board-color-selector></fuse-scrumboard-board-color-selector>
</div>
<!-- / SIDENAV CONTENT -->
</div>
</div>

View File

@@ -0,0 +1,38 @@
:host {
display: flex;
flex-direction: column;
flex: 1 0 auto;
height: 100%;
.views {
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
height: 100%;
.view {
position: absolute;
width: 100%;
height: 100%;
bottom: 0;
left: 0;
right: 0;
top: 0;
display: flex;
flex-direction: column;
> .header {
flex: 0 1 auto;
height: 64px;
min-height: 64px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
> .content {
flex: 1 1 auto;
overflow-y: auto;
}
}
}
}

View File

@@ -0,0 +1,50 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { ScrumboardService } from '../../../scrumboard.service';
import { Animations } from '../../../../../../../core/animations';
@Component({
selector : 'fuse-scrumboard-board-settings',
templateUrl: './settings.component.html',
styleUrls : ['./settings.component.scss'],
animations : [Animations.slideInLeft, Animations.slideInRight]
})
export class FuseScrumboardBoardSettingsSidenavComponent implements OnInit, OnDestroy
{
board: any;
view = 'main';
onBoardChanged: Subscription;
constructor(
private scrumboardService: ScrumboardService
)
{
}
ngOnInit()
{
this.onBoardChanged =
this.scrumboardService.onBoardChanged
.subscribe(board => {
this.board = board;
});
}
toggleCardCover()
{
this.board.settings.cardCoverImages = !this.board.settings.cardCoverImages;
this.scrumboardService.updateBoard();
}
toggleSubcription()
{
this.board.settings.subscribed = !this.board.settings.subscribed;
this.scrumboardService.updateBoard();
}
ngOnDestroy()
{
this.onBoardChanged.unsubscribe();
}
}

View File

@@ -0,0 +1,37 @@
import { FuseUtils } from '../../../../core/fuseUtils';
export class Card
{
id: string;
name: string;
description: string;
idAttachmentCover: string;
idMembers: string[];
idLabels: string[];
attachments: any[];
subscribed: boolean;
checklists: any[];
checkItems: number;
checkItemsChecked: number;
comments: any[];
activities: any[];
due: string;
constructor(card)
{
this.id = card.id || FuseUtils.generateGUID();
this.name = card.name || '';
this.description = card.description || '';
this.idAttachmentCover = card.idAttachmentCover || '';
this.idMembers = card.idMembers || [];
this.idLabels = card.idLabels || [];
this.attachments = card.attachments || [];
this.subscribed = card.subscribed || true;
this.checklists = card.checklists || [];
this.checkItems = card.checkItems || 0;
this.checkItemsChecked = card.checkItemsChecked || 0;
this.comments = card.comments || [];
this.activities = card.activities || [];
this.due = card.due || '';
}
}

View File

@@ -0,0 +1,15 @@
import { FuseUtils } from '../../../../core/fuseUtils';
export class List
{
id: string;
name: string;
idCards: string[];
constructor(list)
{
this.id = list.id || FuseUtils.generateGUID();
this.name = list.name || '';
this.idCards = [];
}
}

View File

@@ -0,0 +1,31 @@
<!-- BOARDS -->
<div id="boards" class="md-primary-400-bg" fxLayout="column" fxLayoutAlign="start center" fxFlex perfect-scrollbar>
<div class="header pt-44 pt-md-88" fxFlex="0 0 auto">
<h1>Scrumboard App</h1>
</div>
<!-- BOARD LIST -->
<div class="board-list" fxFlex="0 0 auto" fxLayout="row" fxLayoutAlign="center center" fxLayoutWrap>
<!-- BOARD -->
<div class="board-list-item" *ngFor="let board of boards"
[routerLink]="'/apps/scrumboard/boards/'+board.id+'/'+board.uri"
fxLayout="column" fxLayoutAlign="center center">
<md-icon class="s-64">assessment</md-icon>
<div class="board-name">{{board.name}}</div>
</div>
<!-- / BOARD -->
<!-- NEW BOARD BUTTON -->
<div class="board-list-item add-new-board" fxLayout="column" fxLayoutAlign="center center"
(click)="newBoard()">
<md-icon class="s-64">add_circle</md-icon>
<div class="board-name">Add new board</div>
</div>
<!-- / NEW BOARD BUTTON -->
</div>
<!-- / BOARD LIST -->
</div>
<!-- / BOARDS -->

View File

@@ -0,0 +1,34 @@
@import "src/app/core/scss/fuse";
:host {
min-height: 100%;
#boards {
width: 100%;
.board-list {
padding: 32px 0;
max-height: none!important;
.board-list-item {
min-width: 210px;
width: 210px;
padding: 24px 0;
margin: 16px;
border-radius: 2px;
background: rgba(0, 0, 0, 0.12);
cursor: pointer;
&:hover {
@include mat-elevation(4);
}
.board-name {
padding-top: 16px;
font-weight: 500;
}
}
}
}
}

View File

@@ -0,0 +1,47 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ScrumboardService } from './scrumboard.service';
import { Subscription } from 'rxjs/Subscription';
import { Router } from '@angular/router';
import { Board } from './board.model';
@Component({
selector : 'fuse-scrumboard',
templateUrl: './scrumboard.component.html',
styleUrls : ['./scrumboard.component.scss']
})
export class FuseScrumboardComponent implements OnInit, OnDestroy
{
boards: any[];
onBoardsChanged: Subscription;
constructor(
private router: Router,
private scrumboardService: ScrumboardService
)
{
}
ngOnInit()
{
this.onBoardsChanged =
this.scrumboardService.onBoardsChanged
.subscribe(boards => {
this.boards = boards;
});
}
newBoard()
{
const newBoard = new Board({});
this.scrumboardService.createNewBoard(newBoard).then(() => {
this.router.navigate(['/apps/scrumboard/boards/' + newBoard.id + '/' + newBoard.uri]);
});
}
ngOnDestroy()
{
this.onBoardsChanged.unsubscribe();
}
}

View File

@@ -0,0 +1,66 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../../../../core/modules/shared.module';
import { RouterModule, Routes } from '@angular/router';
import { FuseScrumboardComponent } from './scrumboard.component';
import { BoardResolve, ScrumboardService } from './scrumboard.service';
import { FuseScrumboardBoardComponent } from './board/board.component';
import { FuseScrumboardBoardListComponent } from './board/list/list.component';
import { FuseScrumboardBoardCardComponent } from './board/list/card/card.component';
import { FuseScrumboardBoardEditListNameComponent } from './board/list/edit-list-name/edit-list-name.component';
import { FuseScrumboardBoardAddCardComponent } from './board/list/add-card/add-card.component';
import { FuseScrumboardBoardAddListComponent } from './board/add-list/add-list.component';
import { FuseScrumboardCardDialogComponent } from './board/dialogs/card/card.component';
import { FuseScrumboardLabelSelectorComponent } from './board/dialogs/card/label-selector/label-selector.component';
import { FuseScrumboardEditBoardNameComponent } from './board/edit-board-name/edit-board-name.component';
import { FuseScrumboardBoardSettingsSidenavComponent } from './board/sidenavs/settings/settings.component';
import { FuseScrumboardBoardColorSelectorComponent } from './board/sidenavs/settings/board-color-selector/board-color-selector.component';
const routes: Routes = [
{
path : 'boards',
component: FuseScrumboardComponent,
resolve : {
scrumboard: ScrumboardService
}
},
{
path : 'boards/:boardId/:boardUri',
component: FuseScrumboardBoardComponent,
resolve : {
board: BoardResolve
}
},
{
path : '**',
redirectTo: 'boards'
}
];
@NgModule({
declarations : [
FuseScrumboardComponent,
FuseScrumboardBoardComponent,
FuseScrumboardBoardListComponent,
FuseScrumboardBoardCardComponent,
FuseScrumboardBoardEditListNameComponent,
FuseScrumboardBoardAddCardComponent,
FuseScrumboardBoardAddListComponent,
FuseScrumboardCardDialogComponent,
FuseScrumboardLabelSelectorComponent,
FuseScrumboardEditBoardNameComponent,
FuseScrumboardBoardSettingsSidenavComponent,
FuseScrumboardBoardColorSelectorComponent
],
imports : [
SharedModule,
RouterModule.forChild(routes)
],
providers : [
ScrumboardService,
BoardResolve
],
entryComponents: [FuseScrumboardCardDialogComponent]
})
export class FuseScrumboardModule
{
}

View File

@@ -0,0 +1,174 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Http } from '@angular/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class ScrumboardService implements Resolve<any>
{
boards: any[];
routeParams: any;
board: any;
onBoardsChanged: BehaviorSubject<any> = new BehaviorSubject([]);
onBoardChanged: BehaviorSubject<any> = new BehaviorSubject([]);
constructor(private http: Http)
{
}
/**
* Resolve
* @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any}
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
this.routeParams = route.params;
return new Promise((resolve, reject) => {
Promise.all([
this.getBoards()
]).then(
() => {
resolve();
},
reject
);
});
}
getBoards(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/scrumboard-boards')
.subscribe(response => {
this.boards = response.json().data;
this.onBoardsChanged.next(this.boards);
resolve(this.boards);
}, reject);
});
}
getBoard(boardId): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/scrumboard-boards/' + boardId)
.subscribe(response => {
this.board = response.json().data;
this.onBoardChanged.next(this.board);
resolve(this.board);
}, reject);
});
}
addCard(listId, newCard)
{
this.board.lists.map((list) => {
if ( list.id === listId )
{
return list.idCards.push(newCard.id);
}
});
this.board.cards.push(newCard);
return this.updateBoard();
}
addList(newList)
{
this.board.lists.push(newList);
return this.updateBoard();
}
removeList(listId)
{
const list = this.board.lists.find((_list) => {
return _list.id === listId;
});
for ( const cardId of list.idCards )
{
this.removeCard(cardId);
}
const index = this.board.lists.indexOf(list);
this.board.lists.splice(index, 1);
return this.updateBoard();
}
removeCard(cardId, listId?)
{
const card = this.board.cards.find((_card) => {
return _card.id === cardId;
});
if ( listId )
{
const list = this.board.lists.find((_list) => {
return listId === _list.id;
});
list.idCards.splice(list.idCards.indexOf(cardId), 1);
}
this.board.cards.splice(this.board.cards.indexOf(card), 1);
this.updateBoard();
}
updateBoard()
{
return new Promise((resolve, reject) => {
this.http.post('api/scrumboard-boards/' + this.board.id, this.board)
.subscribe(response => {
this.onBoardChanged.next(this.board);
resolve(this.board);
}, reject);
});
}
updateCard(newCard)
{
this.board.cards.map((_card) => {
if ( _card.id === newCard.id )
{
return newCard;
}
});
this.updateBoard();
}
createNewBoard(board)
{
return new Promise((resolve, reject) => {
this.http.post('api/scrumboard-boards/' + board.id, board)
.subscribe(response => {
resolve(board);
}, reject);
});
}
}
@Injectable()
export class BoardResolve implements Resolve<any>
{
constructor(private scrumboardService: ScrumboardService)
{
}
resolve(route: ActivatedRouteSnapshot)
{
return this.scrumboardService.getBoard(route.paramMap.get('boardId'));
}
}

View File

@@ -1,4 +1,4 @@
import { Component, OnDestroy, OnInit, ViewChildren } from '@angular/core';
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { TodoService } from '../todo.service';
import { Todo } from '../todo.model';
import { Subscription } from 'rxjs/Subscription';
@@ -16,7 +16,7 @@ export class FuseTodoDetailsComponent implements OnInit, OnDestroy
tags: any[];
formType: string;
todoForm: FormGroup;
@ViewChildren('titleInput') titleInputField;
@ViewChild('titleInput') titleInputField;
onFormChange: any;
onCurrentTodoChanged: Subscription;
@@ -77,7 +77,7 @@ export class FuseTodoDetailsComponent implements OnInit, OnDestroy
focusTitleField()
{
setTimeout(() => {
this.titleInputField.first.nativeElement.focus();
this.titleInputField.nativeElement.focus();
});
}

View File

@@ -1,3 +1,14 @@
<md-toolbar class="mat-elevation-z1">
<span>Footer</span>
<md-toolbar>
<div fxLayout="row" fxLayoutAlign="center center" fxFlex>
<a href="http://themeforest.net/item/fuse-angularjs-material-design-admin-template/12931855?ref=srcn"
target="_blank" md-button class="ml-8 ml-sm-24 md-accent-800-bg" fxFlex="0 0 auto" fxLayout="row"
fxLayoutAlign="start center">
<md-icon class="s-16 mr-sm-4">shopping_cart</md-icon>
<span>Purchase Fuse template (Angular4+)</span>
</a>
</div>
</md-toolbar>

View File

@@ -6,6 +6,7 @@
.mat-toolbar {
background: inherit;
color: inherit;
box-shadow: 0px -1px 1px -1px rgba(0, 0, 0, 0.2), 0px 0px 1px 0px rgba(0, 0, 0, 0.14), 0px -1px 3px 0px rgba(0, 0, 0, 0.12);
}
&.above {

View File

@@ -8,13 +8,22 @@
</ng-container>
<!-- / TOOLBAR: Above -->
<!-- NAVBAR: Top -->
<fuse-navbar-horizontal class="top-navbar" fxHide fxShow.gt-md
[class]="fuseSettings.colorClasses.navbar"
*ngIf="fuseSettings.layout.navigation === 'top'">
</fuse-navbar-horizontal>
<!-- / NAVBAR: Top -->
<div id="wrapper">
<fuse-navbar [folded]="false"
class="left-navbar"
*ngIf="fuseSettings.layout.navigation === 'left'"
[class]="fuseSettings.colorClasses.navbar">
</fuse-navbar>
<!-- NAVBAR: Left -->
<fuse-navbar-vertical [folded]="false"
fxShow [fxHide.gt-md]="fuseSettings.layout.navigation === 'top'"
class="left-navbar" [class]="fuseSettings.colorClasses.navbar"
*ngIf="fuseSettings.layout.navigation === 'left' || fuseSettings.layout.navigation === 'top'">
</fuse-navbar-vertical>
<!-- / NAVBAR: Left -->
<div class="content-wrapper">
@@ -34,7 +43,13 @@
</div>
<fuse-navbar [folded]="false" class="md-primary-700-bg right-navbar" *ngIf="fuseSettings.layout.navigation === 'right'"></fuse-navbar>
<!-- NAVBAR: Right -->
<fuse-navbar-vertical [folded]="false"
class="right-navbar"
[class]="fuseSettings.colorClasses.navbar"
*ngIf="fuseSettings.layout.navigation === 'right'">
</fuse-navbar-vertical>
<!-- / NAVBAR: Right -->
</div>

View File

@@ -6,6 +6,12 @@ fuse-main {
width: 100%;
height: 100%;
&.boxed {
max-width: 1200px;
margin: 0 auto;
@include mat-elevation(8);
}
&.disable-perfect-scrollbar {
.ps {
@@ -58,6 +64,7 @@ fuse-main {
flex: 1;
flex-direction: row;
width: 100%;
overflow: hidden;
> *:not(router-outlet):not(.ps__scrollbar-x-rail):not(.ps__scrollbar-y-rail) {
display: flex;

View File

@@ -13,6 +13,7 @@ export class FuseMainComponent implements OnInit, OnDestroy
onSettingsChanged: Subscription;
fuseSettings: any;
@HostBinding('class.disable-perfect-scrollbar') disableCustomScrollbars;
@HostBinding('class.boxed') boxed;
constructor(
private _renderer: Renderer2,
@@ -26,6 +27,7 @@ export class FuseMainComponent implements OnInit, OnDestroy
(newSettings) => {
this.fuseSettings = newSettings;
this.disableCustomScrollbars = !this.fuseSettings.customScrollbars;
this.boxed = this.fuseSettings.layout.mode === 'boxed';
}
);
}

View File

@@ -6,10 +6,11 @@ import { SharedModule } from '../core/modules/shared.module';
import { FuseMainComponent } from './main.component';
import { FuseContentComponent } from './content/content.component';
import { FuseFooterComponent } from './footer/footer.component';
import { FuseNavbarComponent } from './navbar/navbar.component';
import { FuseNavbarVerticalComponent } from './navbar/vertical/navbar-vertical.component';
import { FuseToolbarComponent } from './toolbar/toolbar.component';
import { FuseNavigationModule } from '../core/components/navigation/navigation.module';
import { FuseNavbarToggleDirective } from './navbar/navbar-toggle.directive';
import { FuseNavbarVerticalToggleDirective } from './navbar/vertical/navbar-vertical-toggle.directive';
import { FuseNavbarHorizontalComponent } from './navbar/horizontal/navbar-horizontal.component';
import { FuseQuickPanelComponent } from './quick-panel/quick-panel.component';
import { FuseThemeOptionsComponent } from '../core/components/theme-options/theme-options.component';
import { FuseShortcutsModule } from '../core/components/shortcuts/shortcuts.module';
@@ -20,9 +21,10 @@ import { FuseSearchBarModule } from '../core/components/search-bar/search-bar.mo
FuseContentComponent,
FuseFooterComponent,
FuseMainComponent,
FuseNavbarComponent,
FuseNavbarVerticalComponent,
FuseNavbarHorizontalComponent,
FuseToolbarComponent,
FuseNavbarToggleDirective,
FuseNavbarVerticalToggleDirective,
FuseThemeOptionsComponent,
FuseQuickPanelComponent
],

View File

@@ -0,0 +1 @@
<fuse-navigation layout="horizontal"></fuse-navigation>

View File

@@ -1,4 +1,4 @@
@import "../../core/scss/fuse";
@import "../../../core/scss/fuse";
fuse-main {
@@ -23,7 +23,7 @@ fuse-main {
}
}
fuse-navbar {
fuse-navbar-vertical {
display: flex;
flex-direction: column;
width: 256px;

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