mirror of
https://github.com/richard-loafle/fuse-angular.git
synced 2025-01-09 20:15:07 +00:00
Merge branch 'scrumboard-app'
This commit is contained in:
commit
31087186f7
43
package-lock.json
generated
43
package-lock.json
generated
|
@ -1183,7 +1183,6 @@
|
|||
"resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
|
||||
"integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"align-text": "0.1.4",
|
||||
"lazy-cache": "1.0.4"
|
||||
|
@ -1193,8 +1192,7 @@
|
|||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
|
||||
"integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -5919,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",
|
||||
|
@ -7922,7 +7925,6 @@
|
|||
"resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
|
||||
"integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"align-text": "0.1.4"
|
||||
}
|
||||
|
@ -9174,28 +9176,48 @@
|
|||
"camelcase": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
|
||||
"integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
|
||||
"integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
|
||||
"dev": true
|
||||
},
|
||||
"cliui": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
|
||||
"integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE="
|
||||
"integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"center-align": "0.1.3",
|
||||
"right-align": "0.1.3",
|
||||
"wordwrap": "0.0.2"
|
||||
}
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "2.8.29",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
|
||||
"integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"source-map": "0.5.6",
|
||||
"uglify-to-browserify": "1.0.2",
|
||||
"yargs": "3.10.0"
|
||||
}
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
|
||||
"integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
|
||||
"integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
|
||||
"integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E="
|
||||
"integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "1.2.1",
|
||||
"cliui": "2.1.0",
|
||||
"decamelize": "1.2.0",
|
||||
"window-size": "0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9908,8 +9930,7 @@
|
|||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
|
||||
"integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "0.0.3",
|
||||
|
|
|
@ -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,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'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<button md-icon-button
|
||||
type="button"
|
||||
class="mat-elevation-z1"
|
||||
[mdMenuTriggerFor]="colorMenu"
|
||||
(onMenuOpen)="onMenuOpen()"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { FuseMdSidenavHelperService } from '../directives/md-sidenav-helper/md-s
|
|||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
787
src/app/fuse-fake-db/scrumboard.ts
Normal file
787
src/app/fuse-fake-db/scrumboard.ts
Normal 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'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
84
src/app/main/content/apps/scrumboard/board.model.ts
Normal file
84
src/app/main/content/apps/scrumboard/board.model.ts
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
@ -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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<md-sidenav-container>
|
||||
|
||||
<div id="board">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header md-accent-bg p-16 p-sm-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-sm-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">
|
||||
<div>
|
||||
<md-icon *ngIf="board.settings.subscribed" class="board-subscribe s-16">remove_red_eye</md-icon>
|
||||
<fuse-scrumboard-edit-board-name
|
||||
fxFlex="1 0 auto"
|
||||
[board]="board"
|
||||
(onNameChanged)="onBoardNameChanged($event)">
|
||||
</fuse-scrumboard-edit-board-name>
|
||||
</div>
|
||||
</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-sm-24">
|
||||
|
||||
<!-- BOARD -->
|
||||
<div class="board-content ngx-dnd-container p-16 p-sm-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>
|
132
src/app/main/content/apps/scrumboard/board/board.component.scss
Normal file
132
src/app/main/content/apps/scrumboard/board/board.component.scss
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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" (submit)="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()">
|
||||
<md-icon>close</md-icon>
|
||||
</button>
|
||||
</form>
|
|
@ -0,0 +1,8 @@
|
|||
:host {
|
||||
.board-name {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 -->
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<div fxFlex="1 0 auto" *ngIf="!formActive" class="list-header-name" (click)="openForm()">
|
||||
{{list.name}}
|
||||
</div>
|
||||
|
||||
<form [formGroup]="form" (submit)="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()">
|
||||
<md-icon>close</md-icon>
|
||||
</button>
|
||||
</form>
|
|
@ -0,0 +1,9 @@
|
|||
:host {
|
||||
.list-header-name {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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(xs) {
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -0,0 +1,10 @@
|
|||
:host {
|
||||
|
||||
.colors {
|
||||
|
||||
.color {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
37
src/app/main/content/apps/scrumboard/card.model.ts
Normal file
37
src/app/main/content/apps/scrumboard/card.model.ts
Normal 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 || '';
|
||||
}
|
||||
}
|
15
src/app/main/content/apps/scrumboard/list.model.ts
Normal file
15
src/app/main/content/apps/scrumboard/list.model.ts
Normal 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 = [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<!-- BOARDS -->
|
||||
<div id="boards" class="md-primary-400-bg" fxLayout="column" fxFlex>
|
||||
|
||||
<!-- BOARD SELECTION -->
|
||||
<div id="board-selector" fxLayout="column" fxLayoutAlign="start center" fxFlex>
|
||||
|
||||
<h1>Scrumboard App</h1>
|
||||
|
||||
<!-- BOARD LIST -->
|
||||
<div class="board-list" 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>
|
||||
<!-- / BOARD SELECTION -->
|
||||
|
||||
</div>
|
||||
<!-- / BOARDS -->
|
187
src/app/main/content/apps/scrumboard/scrumboard.component.scss
Normal file
187
src/app/main/content/apps/scrumboard/scrumboard.component.scss
Normal file
|
@ -0,0 +1,187 @@
|
|||
@import "src/app/core/scss/fuse";
|
||||
|
||||
:host {
|
||||
min-height: 100%;
|
||||
|
||||
#board-selector {
|
||||
margin-top: 88px;
|
||||
|
||||
.board-list {
|
||||
padding: 32px 0;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#scrumboard {
|
||||
height: 100%;
|
||||
|
||||
> .header {
|
||||
position: relative;
|
||||
height: 96px;
|
||||
min-height: 96px;
|
||||
max-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.editable-click {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.editable-wrap {
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
.editable-controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.editable-input {
|
||||
width: inherit;
|
||||
background-color: white;
|
||||
padding: 8px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.editable-buttons {
|
||||
display: inherit;
|
||||
|
||||
.md-button {
|
||||
margin: 0;
|
||||
|
||||
&:first-of-type {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.icon-cancel {
|
||||
color: rgba(0, 0, 0, 0.32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.board-selector-backdrop {
|
||||
z-index: 47;
|
||||
}
|
||||
}
|
||||
|
||||
// RESPONSIVE
|
||||
@include media-breakpoint(xs) {
|
||||
#scrumboard {
|
||||
|
||||
.header {
|
||||
height: 120px;
|
||||
max-height: 120px;
|
||||
min-height: 120px;
|
||||
|
||||
}
|
||||
|
||||
#board-selector {
|
||||
top: 120px;
|
||||
}
|
||||
}
|
||||
}
|
47
src/app/main/content/apps/scrumboard/scrumboard.component.ts
Normal file
47
src/app/main/content/apps/scrumboard/scrumboard.component.ts
Normal 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();
|
||||
}
|
||||
}
|
66
src/app/main/content/apps/scrumboard/scrumboard.module.ts
Normal file
66
src/app/main/content/apps/scrumboard/scrumboard.module.ts
Normal 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
|
||||
{
|
||||
}
|
174
src/app/main/content/apps/scrumboard/scrumboard.service.ts
Normal file
174
src/app/main/content/apps/scrumboard/scrumboard.service.ts
Normal 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'));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,12 @@ export class FuseNavigation
|
|||
'fg' : '#FFFFFF'
|
||||
}
|
||||
},
|
||||
{
|
||||
'title': 'Scrumboard',
|
||||
'type' : 'nav-item',
|
||||
'icon' : 'assessment',
|
||||
'url' : '/apps/scrumboard'
|
||||
},
|
||||
{
|
||||
'title': 'PAGES',
|
||||
'type' : 'subheader'
|
||||
|
|
Loading…
Reference in New Issue
Block a user