commit 4b20cbb154210fdbee9383b3541cd99a311c50c6 Author: Claudiu Farcas Date: Tue Feb 21 05:39:27 2017 +0200 simple steps diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6e87a00 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ce200cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp + +# dependencies +/node_modules +/bower_components + +# IDEs and editors +/.idea +/.vscode +.project +.classpath +.c9/ +*.launch +.settings/ + +# misc +/.sass-cache +/connect.lock +/coverage/* +/libpeerconnection.log +npm-debug.log +testem.log +/typings + +# e2e +/e2e/*.js +/e2e/*.map + +#System Files +.DS_Store +Thumbs.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..ed50c9b --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# kanban2 + +## Project description + +This project is a playground for a Kanban board made with following libraries: + +* Angular 2 (based on angular-cli) +* Firebase via AngualarFire2 +* Bootstrap with ng2-bootstrap + +## Configuration + +You will need to configure your firebase credentials in firebaseConfig.ts. This can be copied +almost directly from the proposed configuration from the Firebase console. + +```angular2html + apiKey: "????", + authDomain: "????", + databaseURL: "???", + storageBucket: "???", + messagingSenderId: "???" +``` + +Initial data: + +In "data" folder there is "kanban2-d5e2a-export.json" file with some initial data +It can be imported in firebase console in a new fresh firebase database. + +![alt text](data/importdata.png "Import initial data") + + + +## Screenshots + +![alt text](data/screenshot01.png "Import initial data") + + + + +#Angular 2 Specifics + +This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.21. + +## Development server +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. + +## Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class`. + +## Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). +Before running the tests make sure you are serving the app via `ng serve`. + +## Deploying to Github Pages + +Run `ng github-pages:deploy` to deploy to Github Pages. + +## Further help + +To get more help on the `angular-cli` use `ng --help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git a/angular-cli.json b/angular-cli.json new file mode 100644 index 0000000..60e12db --- /dev/null +++ b/angular-cli.json @@ -0,0 +1,60 @@ +{ + "project": { + "version": "1.0.0-beta.21", + "name": "kanban2" + }, + "apps": [ + { + "root": "src", + "outDir": "dist", + "assets": [ + "assets", + "favicon.ico" + ], + "index": "index.html", + "main": "main.ts", + "test": "test.ts", + "tsconfig": "tsconfig.json", + "prefix": "app", + "mobile": false, + "styles": [ + "styles.css", + "../node_modules/bootstrap/dist/css/bootstrap.min.css" + ], + "scripts": [], + "environments": { + "source": "environments/environment.ts", + "dev": "environments/environment.ts", + "prod": "environments/environment.prod.ts" + } + } + ], + "addons": [], + "packages": [], + "e2e": { + "protractor": { + "config": "./protractor.conf.js" + } + }, + "test": { + "karma": { + "config": "./karma.conf.js" + } + }, + "defaults": { + "styleExt": "css", + "prefixInterfaces": false, + "inline": { + "style": false, + "template": false + }, + "spec": { + "class": false, + "component": true, + "directive": true, + "module": false, + "pipe": true, + "service": true + } + } +} diff --git a/data/importdata.png b/data/importdata.png new file mode 100644 index 0000000..05ad71d Binary files /dev/null and b/data/importdata.png differ diff --git a/data/kanban2-d5e2a-export.json b/data/kanban2-d5e2a-export.json new file mode 100644 index 0000000..79b07f2 --- /dev/null +++ b/data/kanban2-d5e2a-export.json @@ -0,0 +1,146 @@ +{ + "cardlist" : { + "-KdRl7OguO3iT-Uapu1B" : { + "color" : "orange", + "created_at" : "Mon Feb 20 2017 20:24:25 GMT+0200 (GTB Standard Time)", + "name" : "IN PPROGRESS", + "order" : 1, + "projectId" : "-KdRh_nFQT3Im1KfUPMW" + }, + "-KdRmdEq-T-AmCqRvPQq" : { + "color" : "green", + "created_at" : "Mon Feb 20 2017 20:31:02 GMT+0200 (GTB Standard Time)", + "name" : "DONE", + "order" : 2, + "projectId" : "-KdRh_nFQT3Im1KfUPMW" + }, + "ToDo" : { + "color" : "gray", + "created_at" : "Mon Feb 20 2017 20:31:02 GMT+0200 (GTB Standard Time)", + "name" : "TO DO", + "order" : 0, + "projectId" : "-KdRh_nFQT3Im1KfUPMW" + } + }, + "cards" : { + "-KdTXIRPx0OAe8V9tfba" : { + "cardListId" : "ToDo", + "created_at" : "Tue Feb 21 2017 04:38:52 GMT+0200 (GTB Standard Time)", + "description" : "Read it before the Friday presentation, discuss ideas with the team.", + "isExpanded" : true, + "name" : "Read \"Don't make me think\"", + "order" : 0 + }, + "-KdTYCqtrRKoZejMUQaA" : { + "cardListId" : "ToDo", + "created_at" : "Tue Feb 21 2017 04:42:52 GMT+0200 (GTB Standard Time)", + "description" : "Test driven development desc", + "isExpanded" : false, + "name" : "Test driven development", + "order" : 0 + }, + "-KdT_XMJHSc5EEdMbZPL" : { + "cardListId" : "-KdRl7OguO3iT-Uapu1B", + "created_at" : "Tue Feb 21 2017 04:53:00 GMT+0200 (GTB Standard Time)", + "description" : "Read tech articles on medium desc", + "isExpanded" : false, + "name" : "Read tech articles on medium", + "order" : 0 + }, + "-KdT_hWu_JChDp22_yYe" : { + "cardListId" : "-KdRl7OguO3iT-Uapu1B", + "created_at" : "Tue Feb 21 2017 04:53:46 GMT+0200 (GTB Standard Time)", + "description" : "Write code with the supervision of another colleague.", + "isExpanded" : true, + "name" : "Pair programming", + "order" : 0 + }, + "-KdT_vWZ6zbKoG4ubmgF" : { + "cardListId" : "-KdRl7OguO3iT-Uapu1B", + "created_at" : "Tue Feb 21 2017 04:54:43 GMT+0200 (GTB Standard Time)", + "description" : "Read on time management desc", + "isExpanded" : false, + "name" : "Read on time management", + "order" : 0 + }, + "-KdTaA4uy_7iFCw0Bzr5" : { + "cardListId" : "-KdRmdEq-T-AmCqRvPQq", + "created_at" : "Tue Feb 21 2017 04:55:47 GMT+0200 (GTB Standard Time)", + "description" : "Have a meeting with the team in order to get our definitions on Agile aligned.", + "isExpanded" : true, + "name" : "Meeting on Agile", + "order" : 0 + } + }, + "projects" : { + "-KdRh_nFQT3Im1KfUPMW" : { + "created_at" : "Mon Feb 20 2017 20:08:59 GMT+0200 (GTB Standard Time)", + "name" : "TestProject1" + } + }, + "tasks" : { + "-KdT_iknV-6SxGejJkZt" : { + "cardId" : "-KdT_hWu_JChDp22_yYe", + "created_at" : "Tue Feb 21 2017 04:53:51 GMT+0200 (GTB Standard Time)", + "description" : "Write code", + "isCompleted" : true, + "order" : 0 + }, + "-KdT_jkICL5Q6SwKZ25c" : { + "cardId" : "-KdT_hWu_JChDp22_yYe", + "created_at" : "Tue Feb 21 2017 04:53:55 GMT+0200 (GTB Standard Time)", + "description" : "Check code", + "isCompleted" : false, + "order" : 0 + }, + "-KdT_mNQeZ0BmJvM-Xay" : { + "cardId" : "-KdTXIRPx0OAe8V9tfba", + "created_at" : "Tue Feb 21 2017 04:54:06 GMT+0200 (GTB Standard Time)", + "description" : "Read book", + "isCompleted" : false, + "order" : 0 + }, + "-KdT_oNftsSW6QX0mgYB" : { + "cardId" : "-KdTXIRPx0OAe8V9tfba", + "created_at" : "Tue Feb 21 2017 04:54:14 GMT+0200 (GTB Standard Time)", + "description" : "Find similar examples", + "isCompleted" : false, + "order" : 0 + }, + "-KdT_q8SgYfNs4USoVh3" : { + "cardId" : "-KdTXIRPx0OAe8V9tfba", + "created_at" : "Tue Feb 21 2017 04:54:21 GMT+0200 (GTB Standard Time)", + "description" : "Prepare presentation", + "isCompleted" : false, + "order" : 0 + }, + "-KdTaDPoEdDcMr9PujkO" : { + "cardId" : "-KdTaA4uy_7iFCw0Bzr5", + "created_at" : "Tue Feb 21 2017 04:56:00 GMT+0200 (GTB Standard Time)", + "description" : "Define Agile methodology", + "isCompleted" : true, + "order" : 0 + }, + "-KdTaFqonDXBEef6F-3o" : { + "cardId" : "-KdTaA4uy_7iFCw0Bzr5", + "created_at" : "Tue Feb 21 2017 04:56:10 GMT+0200 (GTB Standard Time)", + "description" : "What is Scrum?", + "isCompleted" : true, + "order" : 0 + }, + "-KdTaHSJYL0uviZyvOST" : { + "cardId" : "-KdTaA4uy_7iFCw0Bzr5", + "created_at" : "Tue Feb 21 2017 04:56:17 GMT+0200 (GTB Standard Time)", + "description" : "Current implementation", + "isCompleted" : true, + "order" : 0 + }, + "-KdTaKOLbyxnCrJz4GVs" : { + "cardId" : "-KdTaA4uy_7iFCw0Bzr5", + "created_at" : "Tue Feb 21 2017 04:56:29 GMT+0200 (GTB Standard Time)", + "description" : "What to improve?", + "isCompleted" : true, + "order" : 0 + } + } +} diff --git a/data/screenshot01.png b/data/screenshot01.png new file mode 100644 index 0000000..221ae3a Binary files /dev/null and b/data/screenshot01.png differ diff --git a/e2e/app.e2e-spec.ts b/e2e/app.e2e-spec.ts new file mode 100644 index 0000000..11804a8 --- /dev/null +++ b/e2e/app.e2e-spec.ts @@ -0,0 +1,14 @@ +import { ValuationJsPage } from './app.po'; + +describe('kanban2 App', function() { + let page: ValuationJsPage; + + beforeEach(() => { + page = new ValuationJsPage(); + }); + + it('should display message saying app works', () => { + page.navigateTo(); + expect(page.getParagraphText()).toEqual('app works!'); + }); +}); diff --git a/e2e/app.po.ts b/e2e/app.po.ts new file mode 100644 index 0000000..c07b268 --- /dev/null +++ b/e2e/app.po.ts @@ -0,0 +1,11 @@ +import { browser, element, by } from 'protractor'; + +export class ValuationJsPage { + navigateTo() { + return browser.get('/'); + } + + getParagraphText() { + return element(by.css('app-root h1')).getText(); + } +} diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json new file mode 100644 index 0000000..656bdb1 --- /dev/null +++ b/e2e/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "declaration": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "module": "commonjs", + "moduleResolution": "node", + "outDir": "../dist/out-tsc-e2e", + "sourceMap": true, + "target": "es5", + "typeRoots": [ + "../node_modules/@types" + ] + } +} diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..1f2613a --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/0.13/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', 'angular-cli'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-remap-istanbul'), + require('angular-cli/plugins/karma') + ], + files: [ + { pattern: './src/test.ts', watched: false } + ], + preprocessors: { + './src/test.ts': ['angular-cli'] + }, + mime: { + 'text/x-typescript': ['ts','tsx'] + }, + remapIstanbulReporter: { + reports: { + html: 'coverage', + lcovonly: './coverage/coverage.lcov' + } + }, + angularCli: { + config: './angular-cli.json', + environment: 'dev' + }, + reporters: config.angularCli && config.angularCli.codeCoverage + ? ['progress', 'karma-remap-istanbul'] + : ['progress'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false + }); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..f72946a --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "kanban2", + "version": "0.0.0", + "license": "MIT", + "angular-cli": {}, + "scripts": { + "start": "ng serve", + "lint": "tslint \"src/**/*.ts\"", + "test": "ng test", + "pree2e": "webdriver-manager update", + "e2e": "protractor" + }, + "private": true, + "dependencies": { + "@angular/common": "2.4.7", + "@angular/compiler": "2.4.7", + "@angular/core": "2.4.7", + "@angular/forms": "2.4.7", + "@angular/http": "2.4.7", + "@angular/platform-browser": "2.4.7", + "@angular/platform-browser-dynamic": "2.4.7", + "@angular/router": "3.4.7", + "angularfire2": "^2.0.0-beta.7", + "bootstrap": "3.3.7", + "core-js": "2.4.1", + "firebase": "3.6.9", + "ng2-bootstrap": "^1.3.3", + "ng2-dnd": "^2.2.2", + "rxjs": "5.0.3", + "ts-helpers": "1.1.2", + "zone.js": "0.7.6" + }, + "devDependencies": { + "@angular/compiler-cli": "2.4.7", + "@types/jasmine": "2.5.38", + "@types/node": "^6.0.60", + "angular-cli": "1.0.0-beta.28.3", + "codelyzer": "~1.0.0-beta.3", + "jasmine-core": "2.5.2", + "jasmine-spec-reporter": "2.5.0", + "karma": "1.2.0", + "karma-chrome-launcher": "2.0.0", + "karma-cli": "1.0.1", + "karma-jasmine": "1.0.2", + "karma-remap-istanbul": "0.2.1", + "protractor": "4.0.13", + "ts-node": "1.2.1", + "tslint": "4.2.0", + "typescript": "~2.1.6" + } +} diff --git a/protractor.conf.js b/protractor.conf.js new file mode 100644 index 0000000..169743b --- /dev/null +++ b/protractor.conf.js @@ -0,0 +1,32 @@ +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/docs/referenceConf.js + +/*global jasmine */ +var SpecReporter = require('jasmine-spec-reporter'); + +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './e2e/**/*.e2e-spec.ts' + ], + capabilities: { + 'browserName': 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + useAllAngular2AppRoots: true, + beforeLaunch: function() { + require('ts-node').register({ + project: 'e2e' + }); + }, + onPrepare: function() { + jasmine.getEnv().addReporter(new SpecReporter()); + } +}; diff --git a/src/app/app.component.css b/src/app/app.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/app.component.html b/src/app/app.component.html new file mode 100644 index 0000000..c952a6b --- /dev/null +++ b/src/app/app.component.html @@ -0,0 +1,12 @@ +
+

+ {{title}} +

+
+ +
+ + +
+
+
\ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts new file mode 100644 index 0000000..e850680 --- /dev/null +++ b/src/app/app.component.ts @@ -0,0 +1,67 @@ +import {Component, OnInit} from "@angular/core"; +import {DataService} from "app/shared/data.service"; +import {Observable} from "rxjs"; + +import {Project} from "app/models/project-info"; +import {CardList} from "app/models/cardlist-info"; +import {Card} from "app/models/card-info"; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent implements OnInit { + title = 'The Kanban Board'; + projects: Project[]; + cardlists: CardList[]; + + constructor(private dataService: DataService) { + } + + ngOnInit(){ + this.dataService.getProjects() + .subscribe(data => { + this.projects = data; + let firstProject = this.projects[0]; + //console.log(firstProject); + // this.addAddCardList( + // 'Done', + // firstProject.$key, + // 'green' + // ); + }); + this.dataService.getCardLists() + .subscribe(c => this.cardlists = c) + ; + this.dataService.getCards(); + this.dataService.getTasks(); + //this.addProject("TestProject1"); + } + + addProject(name: string) + { + let created_at = new Date().toString(); + let newProject:Project = new Project(); + newProject.name = name; + newProject.created_at= created_at; + this.dataService.addProject(newProject); + } + + addCardList( + name: string, + projectId: string, + color: string, + order: number) + { + let created_at = new Date().toString(); + let newCardList:CardList = new CardList(); + newCardList.name = name; + newCardList.projectId = projectId; + newCardList.color = color; + newCardList.order = order; + newCardList.created_at = created_at; + this.dataService.addCardList(newCardList); + } + +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts new file mode 100644 index 0000000..9068be1 --- /dev/null +++ b/src/app/app.module.ts @@ -0,0 +1,36 @@ +import {BrowserModule} from "@angular/platform-browser"; +import {NgModule} from "@angular/core"; +import {FormsModule, ReactiveFormsModule} from "@angular/forms"; +import {HttpModule} from "@angular/http"; +import {AppComponent} from "./app.component"; +import {authConfig, firebaseConfig} from "environments/firebaseConfig"; +import {AngularFireModule} from "angularfire2"; +import {AlertModule} from "ng2-bootstrap"; +import {ModalModule} from 'ng2-bootstrap'; + +import {DataService} from "app/shared/data.service"; +import {CardListComponent} from "app/cardlist/cardlist.component"; +import {CardComponent} from "app/card/card.component"; +import {DndModule} from 'ng2-dnd'; + +@NgModule({ + declarations: [ + AppComponent, + CardListComponent, + CardComponent + ], + imports: [ + BrowserModule, + FormsModule, + ReactiveFormsModule, + HttpModule, + AlertModule.forRoot(), + ModalModule.forRoot(), + DndModule.forRoot(), + AngularFireModule.initializeApp(firebaseConfig, authConfig) + ], + providers: [DataService], + bootstrap: [AppComponent] +}) +export class AppModule { +} diff --git a/src/app/card/card.component.css b/src/app/card/card.component.css new file mode 100644 index 0000000..8c0217d --- /dev/null +++ b/src/app/card/card.component.css @@ -0,0 +1,38 @@ +.cardTitle{ + margin-left: 10px; + font-size: 1em; + font-weight: bold; +} +.cardDesc{ + margin-left: 10px; +} +.tasklist{ + margin: 10px; +} +.newtask +{ + font-size: 0.8em; +} +.link{ + cursor: pointer; +} +.carret{ + display: inline-block; + cursor: pointer; +} +.titleText{ + display: inline-block; +} +.modal-header { + padding:9px 15px; + border-bottom:1px solid #eee; + background-color: #ff4040; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + color: white; + font-weight: bold; + } \ No newline at end of file diff --git a/src/app/card/card.component.html b/src/app/card/card.component.html new file mode 100644 index 0000000..b696e0c --- /dev/null +++ b/src/app/card/card.component.html @@ -0,0 +1,48 @@ +
+
+ + +
+
+ {{item.name}} +
+
+
+
+ {{item.description}} +
+
+
+ + + {{task.description}} + + +
+
+
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/src/app/card/card.component.ts b/src/app/card/card.component.ts new file mode 100644 index 0000000..c66fe77 --- /dev/null +++ b/src/app/card/card.component.ts @@ -0,0 +1,65 @@ +import {Component, OnInit, Input, ViewChild} from "@angular/core"; +import {DataService} from "app/shared/data.service"; +import {Observable} from "rxjs"; +import {CardList} from "app/models/cardlist-info"; +import {Card} from "app/models/card-info"; +import {Task} from "app/models/task-info"; +import { ModalDirective } from 'ng2-bootstrap/modal'; + +@Component({ + selector: 'card', + templateUrl: './card.component.html', + styleUrls: ['./card.component.css'] +}) +export class CardComponent implements OnInit { + @ViewChild('childModal') public childModal:ModalDirective; + @Input() item: Card; + tasks : Task[] + + newtaskdesc; + + + constructor(private dataService: DataService) { + //console.log(this.item); + } + + ngOnInit() { + //console.log(this.item); + this.dataService.getTasksByCardId(this.item.$key) + .subscribe(data => { + this.tasks = data; + }) + } + + addNewTask(){ + //console.log('Add new subtask!'); + let newTask = new Task(); + newTask.cardId = this.item.$key; + newTask.description = this.newtaskdesc; + newTask.isCompleted = false; + newTask.order = 0; + newTask.created_at = new Date().toString(); + this.dataService.addTask(newTask) + .then(() => { + this.newtaskdesc = ''; + }); + } + + deleteTask(task){ + //console.log(task); + this.childModal.show(); + } + public hideChildModal():void { + this.childModal.hide(); + } + + changeTaskCompleted(task){ + //console.log(task); + this.dataService.updateTask(task.$key, task); + } + + clickCarret(){ + this.item.isExpanded = !this.item.isExpanded; + this.dataService.updateCard(this.item.$key,this.item); + } +} diff --git a/src/app/cardlist/cardlist.component.css b/src/app/cardlist/cardlist.component.css new file mode 100644 index 0000000..ca06fec --- /dev/null +++ b/src/app/cardlist/cardlist.component.css @@ -0,0 +1,86 @@ +.card{ + background-color: lightblue; + border: solid 1px; + border-left-width: 5px; + margin: 15px; +} +.cardTitle{ + margin-left: 10px; + font-size: 1em; +} +.cardDesc{ + margin-left: 10px; +} +.createCard{ + padding: 10px; + /*background-color: lightcyan;*/ +} +.fullScreen{ + position: fixed; + width: 100%; + height: 100%; + left: 0; + top: 0; + clear: both; + background: black; + opacity: 0.3; + padding-left: 35%; + padding-right: 35%; + padding-top: 35%; + padding-bottom: 15%; + z-index: 100; +} +.fullScreentransparent{ + position: absolute; + clear: both; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: transparent; + z-index: 101; +} +.link{ + cursor: pointer; +} +.createTitle{ + font-weight: bold; + color: white; +} +.formfields{ + margin-top: 10px; +} +.inline{ + display: inline-block; +} +.listTitle{ + font-weight: bold; + font-size: 1.2em; +} + + + +.dnd-drag-start { + -moz-transform:scale(0.8); + -webkit-transform:scale(0.8); + transform:scale(0.8); + opacity:0.7; + border: 2px dashed #000; +} + +.dnd-drag-enter { + opacity:0.7; + border: 2px dashed #000; +} + +.dnd-drag-over { + border: 2px dashed #000; +} + +.dnd-sortable-drag { + -moz-transform:scale(0.9); + -webkit-transform:scale(0.9); + transform:scale(0.9); + opacity:0.7; + border: 1px dashed #000; +} \ No newline at end of file diff --git a/src/app/cardlist/cardlist.component.html b/src/app/cardlist/cardlist.component.html new file mode 100644 index 0000000..1b3de74 --- /dev/null +++ b/src/app/cardlist/cardlist.component.html @@ -0,0 +1,53 @@ +
+
+
+ + + {{item.name}} + + +
+
+
+
    +
  • + + +
  • +
+
+
+ +
+
+
+
+
+

New task - {{ item.name }} + +

+
+
+ + + + + +
+
+ +
+
+
\ No newline at end of file diff --git a/src/app/cardlist/cardlist.component.ts b/src/app/cardlist/cardlist.component.ts new file mode 100644 index 0000000..67a9ce9 --- /dev/null +++ b/src/app/cardlist/cardlist.component.ts @@ -0,0 +1,107 @@ +import {Component, OnInit, Input} from "@angular/core"; +import {DataService} from "app/shared/data.service"; +import {Observable} from "rxjs"; +import {CardList} from "app/models/cardlist-info"; +import {Card} from "app/models/card-info"; +import {Task} from "app/models/task-info"; + +@Component({ + selector: 'cardlist', + templateUrl: './cardlist.component.html', + styleUrls: ['./cardlist.component.css'] +}) +export class CardListComponent implements OnInit { + @Input() item: CardList; + cards : Card[] + + toShowAddCard:boolean; + editCard: Card; + cardname; + carddescription; + allowedDropFrom = []; + allowedDragTo = false; + + + constructor(private dataService: DataService) { + } + + ngOnInit() { + this.dataService.getCardsByListId(this.item.$key) + .subscribe(data => { + this.cards = data; + }); + //fill allowed drop-from containers + this.dataService.getCardListsByOrder(this.item.order-1) + .subscribe(d => { + if(d.length>0) + this.allowedDropFrom.push(d[0].$key); + } + ); + //fill if it has next containers + this.dataService.getCardListsByOrder(this.item.order+1) + .subscribe(d => { + if(d.length>0) + this.allowedDragTo = true; + } + ); + } + + showAddCard(){ + this.cardname = ''; + this.carddescription = ''; + this.toShowAddCard = true; + } + + cancelAddCard(){ + this.toShowAddCard = false; + } + saveAddCard(){ + //console.log('save card'); + this.addCard( + this.cardname, + this.carddescription, + true, + this.item.$key, + 0); + this.toShowAddCard = false; + } + + + addCard( + name: string, + description: string, + isExpanded: boolean, + cardListId: string, + order: number + ) + { + let created_at = new Date().toString(); + let newCard:Card = new Card(); + newCard.name = name; + newCard.description = description; + newCard.cardListId = cardListId; + newCard.isExpanded = isExpanded; + newCard.order = order; + newCard.created_at = created_at; + this.dataService.addCard(newCard); + } + + cardDropped(ev){ + let card:Card = ev.dragData; + if(card.cardListId !== this.item.$key){ + card.cardListId = this.item.$key; + this.dataService.updateCard(card.$key, card); + } + } + + allowDragFunction(card: Card){ + return this.allowedDragTo; + } + + allowDropFunction(): any { + return (dragData: Card) => { + return this.allowedDropFrom.indexOf(dragData.cardListId) > -1; + }; + } + +} diff --git a/src/app/index.ts b/src/app/index.ts new file mode 100644 index 0000000..875bdb2 --- /dev/null +++ b/src/app/index.ts @@ -0,0 +1,2 @@ +export * from './app.component'; +export * from './app.module'; diff --git a/src/app/models/card-info.ts b/src/app/models/card-info.ts new file mode 100644 index 0000000..2135ae4 --- /dev/null +++ b/src/app/models/card-info.ts @@ -0,0 +1,9 @@ +export class Card { + $key?: string; + name?: string; + description?: string; + cardListId?: string; + isExpanded?: boolean; + order?: number; + created_at?: string; +} diff --git a/src/app/models/cardlist-info.ts b/src/app/models/cardlist-info.ts new file mode 100644 index 0000000..efa3cf2 --- /dev/null +++ b/src/app/models/cardlist-info.ts @@ -0,0 +1,8 @@ +export class CardList { + $key?: string; + name?: string; + projectId?: string; + color?: string; + order?: number; + created_at?: string; +} diff --git a/src/app/models/project-info.ts b/src/app/models/project-info.ts new file mode 100644 index 0000000..9b2a026 --- /dev/null +++ b/src/app/models/project-info.ts @@ -0,0 +1,5 @@ +export class Project { + $key?: string; + name?: string; + created_at?: string; +} diff --git a/src/app/models/task-info.ts b/src/app/models/task-info.ts new file mode 100644 index 0000000..969a699 --- /dev/null +++ b/src/app/models/task-info.ts @@ -0,0 +1,8 @@ +export class Task { + $key?: string; + description?: string; + isCompleted: boolean; + cardId?: string; + order?: number; + created_at?: string; +} diff --git a/src/app/shared/data.service.ts b/src/app/shared/data.service.ts new file mode 100644 index 0000000..9fc5333 --- /dev/null +++ b/src/app/shared/data.service.ts @@ -0,0 +1,122 @@ +import {Injectable, EventEmitter, Output} from "@angular/core"; +import {AngularFire, FirebaseObjectObservable, FirebaseListObservable} from "angularfire2"; +import {BehaviorSubject} from "rxjs/BehaviorSubject"; +import {Observable, Subject, ReplaySubject, AsyncSubject} from "rxjs"; +import {Project} from "../models/project-info"; +import {CardList} from "../models/cardlist-info"; +import {Card} from "../models/card-info"; +import {Task} from "../models/task-info"; + +@Injectable() +export class DataService { + + projects: FirebaseListObservable; + cardlists: FirebaseListObservable; + cards: FirebaseListObservable; + tasks: FirebaseListObservable; + + constructor(private af: AngularFire) { + //console.log("DataService"); + } + + getProjects(){ + this.projects = this.af.database.list('/projects') as + FirebaseListObservable; + return this.projects; + } + + addProject(project) { + return this.projects.push(project); + } + + + + getCardLists(){ + this.cardlists = this.af.database.list('/cardlist',{ + query: { + orderByChild: 'order' + }} + ) as + FirebaseListObservable; + return this.cardlists; + } + getCardListsById(cardListId:string): FirebaseObjectObservable { + return this.af.database.object(`/cardlist/${cardListId}`) as FirebaseObjectObservable; + } + getCardListsByOrder(order:number): FirebaseListObservable { + let _cardlist = this.af.database.list('/cardlist',{ + query: { + orderByChild: 'order', + equalTo: order, + }} + ) as FirebaseListObservable; + return _cardlist; + } + getCachedCardListsById(cardListId:string):CardList { + return this.cardlists + .filter(d => d.$key == cardListId) + .map(d=> d.$key) + ; + //.first(); + } + getCardListsByProject(projectId: string){ + let _cardlist = this.af.database.list('/cardlist',{ + query: { + orderByChild: 'projectId', + equalTo: projectId, + }} + ) as FirebaseListObservable; + return _cardlist + } + addCardList(cardlist){ + return this.cardlists.push(cardlist); + } + + + + + getCards(){ + this.cards = this.af.database.list('/cards') as + FirebaseListObservable; + return this.cards; + } + getCardsByListId(listId:string){ + this.cards = this.af.database.list('/cards',{ + query: { + orderByChild: 'cardListId', + equalTo: listId, + }} + ) as + FirebaseListObservable; + return this.cards; + } + addCard(card){ + return this.cards.push(card); + } + updateCard(key, updCard){ + return this.cards.update(key, updCard); + } + + + + getTasks(){ + this.tasks = this.af.database.list('/tasks') as + FirebaseListObservable; + return this.cards; + } + getTasksByCardId(cardId:string){ + let _tasks = this.af.database.list('/tasks',{ + query: { + orderByChild: 'cardId', + equalTo: cardId, + }} + ) as FirebaseListObservable; + return _tasks; + } + addTask(task){ + return this.tasks.push(task); + } + updateTask(key, updTask){ + return this.tasks.update(key, updTask); + } +} \ No newline at end of file diff --git a/src/assets/.gitkeep b/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts new file mode 100644 index 0000000..3612073 --- /dev/null +++ b/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100644 index 0000000..00313f1 --- /dev/null +++ b/src/environments/environment.ts @@ -0,0 +1,8 @@ +// The file contents for the current environment will overwrite these during build. +// The build system defaults to the dev environment which uses `environment.ts`, but if you do +// `ng build --env=prod` then `environment.prod.ts` will be used instead. +// The list of which env maps to which file can be found in `angular-cli.json`. + +export const environment = { + production: false +}; diff --git a/src/environments/firebaseConfig.ts b/src/environments/firebaseConfig.ts new file mode 100644 index 0000000..e04402a --- /dev/null +++ b/src/environments/firebaseConfig.ts @@ -0,0 +1,18 @@ + +import {AuthMethods, AuthProviders} from "angularfire2"; + + +export const firebaseConfig = { + //get these from your created firebase project at https://console.firebase.google.com + // Paste all this from the Firebase console... + apiKey: "", + authDomain: "", + databaseURL: "", + storageBucket: "", + messagingSenderId: "" +}; + +export const authConfig = { + provider: AuthProviders.Password, + method: AuthMethods.Password +}; diff --git a/src/favicon.ico b/src/favicon.ico new file mode 100644 index 0000000..8081c7c Binary files /dev/null and b/src/favicon.ico differ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..f39f80d --- /dev/null +++ b/src/index.html @@ -0,0 +1,15 @@ + + + + + The Kanban Board + + + + + + + + Loading... + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..5c3c520 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,12 @@ +import './polyfills.ts'; + +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { enableProdMode } from '@angular/core'; +import { environment } from './environments/environment'; +import { AppModule } from './app/'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/src/polyfills.ts b/src/polyfills.ts new file mode 100644 index 0000000..3b4c55b --- /dev/null +++ b/src/polyfills.ts @@ -0,0 +1,19 @@ +// This file includes polyfills needed by Angular 2 and is loaded before +// the app. You can add your own extra polyfills to this file. +import 'core-js/es6/symbol'; +import 'core-js/es6/object'; +import 'core-js/es6/function'; +import 'core-js/es6/parse-int'; +import 'core-js/es6/parse-float'; +import 'core-js/es6/number'; +import 'core-js/es6/math'; +import 'core-js/es6/string'; +import 'core-js/es6/date'; +import 'core-js/es6/array'; +import 'core-js/es6/regexp'; +import 'core-js/es6/map'; +import 'core-js/es6/set'; +import 'core-js/es6/reflect'; + +import 'core-js/es7/reflect'; +import 'zone.js/dist/zone'; diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..90d4ee0 --- /dev/null +++ b/src/styles.css @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/src/test.ts b/src/test.ts new file mode 100644 index 0000000..81af890 --- /dev/null +++ b/src/test.ts @@ -0,0 +1,32 @@ +import './polyfills.ts'; + +import 'zone.js/dist/long-stack-trace-zone'; +import 'zone.js/dist/proxy.js'; +import 'zone.js/dist/sync-test'; +import 'zone.js/dist/jasmine-patch'; +import 'zone.js/dist/async-test'; +import 'zone.js/dist/fake-async-test'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. +declare var __karma__: any; +declare var require: any; + +// Prevent Karma from running prematurely. +__karma__.loaded = function () {}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +let context = require.context('./', true, /\.spec\.ts/); +// And load the modules. +context.keys().map(context); +// Finally, start Karma to run the tests. +__karma__.start(); diff --git a/src/tsconfig.json b/src/tsconfig.json new file mode 100644 index 0000000..1cf713a --- /dev/null +++ b/src/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "baseUrl": "", + "declaration": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": ["es6", "dom"], + "mapRoot": "./", + "module": "es6", + "moduleResolution": "node", + "outDir": "../dist/out-tsc", + "sourceMap": true, + "target": "es5", + "typeRoots": [ + "../node_modules/@types" + ] + } +} diff --git a/src/typings.d.ts b/src/typings.d.ts new file mode 100644 index 0000000..ea52695 --- /dev/null +++ b/src/typings.d.ts @@ -0,0 +1,2 @@ +// Typings reference file, you can add your own global typings here +// https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..ad0093e --- /dev/null +++ b/tslint.json @@ -0,0 +1,114 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "eofline": true, + "forin": true, + "indent": [ + true, + "spaces" + ], + "label-position": true, + "label-undefined": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + "static-before-instance", + "variables-before-functions" + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-key": true, + "no-duplicate-variable": true, + "no-empty": false, + "no-eval": true, + "no-inferrable-types": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unused-expression": true, + "no-unused-variable": true, + "no-unreachable": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + + "directive-selector-prefix": [true, "app"], + "component-selector-prefix": [true, "app"], + "directive-selector-name": [true, "camelCase"], + "component-selector-name": [true, "kebab-case"], + "directive-selector-type": [true, "attribute"], + "component-selector-type": [true, "element"], + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "component-class-suffix": true, + "directive-class-suffix": true, + "templates-use-public": true, + "invoke-injectable": true + } +}