├── .gitignore ├── README.md ├── backend ├── .gitignore ├── .rspec ├── .rubocop.yml ├── Gemfile ├── Gemfile.lock ├── README.rdoc ├── Rakefile ├── app │ ├── assets │ │ └── images │ │ │ └── .keep │ ├── controllers │ │ ├── application_controller.rb │ │ ├── concerns │ │ │ └── .keep │ │ ├── sessions_controller.rb │ │ ├── tasks_controller.rb │ │ └── users_controller.rb │ ├── mailers │ │ └── .keep │ └── models │ │ ├── .keep │ │ ├── concerns │ │ └── .keep │ │ ├── task.rb │ │ └── user.rb ├── bin │ ├── bundle │ ├── rails │ ├── rake │ ├── setup │ └── spring ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── filter_parameter_logging.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ └── en.yml │ ├── routes.rb │ └── secrets.yml ├── db │ ├── migrate │ │ ├── 20160323123002_create_tasks.rb │ │ └── 20160324104001_create_users.rb │ ├── schema.rb │ └── seeds.rb ├── lib │ ├── assets │ │ └── .keep │ └── tasks │ │ └── .keep ├── log │ └── .keep ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── favicon.ico │ └── robots.txt └── spec │ ├── controllers │ ├── sessions_controller_spec.rb │ ├── tasks_controller_spec.rb │ └── users_controller_spec.rb │ ├── factories │ ├── tasks.rb │ └── users.rb │ ├── rails_helper.rb │ ├── spec_helper.rb │ └── support │ ├── controller_macros.rb │ └── request_helpers.rb └── frontend ├── .editorconfig ├── .jshintrc ├── .stylelintrc ├── Procfile ├── README.md ├── app.js ├── gulpfile.ts ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── test-main.js ├── tools ├── README.md ├── config.ts ├── config │ ├── project.config.ts │ ├── seed.config.interfaces.ts │ └── seed.config.ts ├── debug.ts ├── manual_typings │ ├── project │ │ └── sample.package.d.ts │ └── seed │ │ ├── angular2-hot-loader.d.ts │ │ ├── autoprefixer.d.ts │ │ ├── colorguard.d.ts │ │ ├── connect-livereload.d.ts │ │ ├── cssnano.d.ts │ │ ├── doiuse.d.ts │ │ ├── istream.d.ts │ │ ├── karma.d.ts │ │ ├── merge-stream.d.ts │ │ ├── open.d.ts │ │ ├── postcss-reporter.d.ts │ │ ├── slash.d.ts │ │ ├── stylelint.d.ts │ │ ├── systemjs-builder.d.ts │ │ └── tiny-lr.d.ts ├── tasks │ ├── project │ │ └── sample.task.ts │ └── seed │ │ ├── build.assets.dev.ts │ │ ├── build.assets.prod.ts │ │ ├── build.bundles.app.ts │ │ ├── build.bundles.ts │ │ ├── build.docs.ts │ │ ├── build.html_css.ts │ │ ├── build.index.dev.ts │ │ ├── build.index.prod.ts │ │ ├── build.js.dev.ts │ │ ├── build.js.e2e.ts │ │ ├── build.js.prod.ts │ │ ├── build.js.test.ts │ │ ├── build.js.tools.ts │ │ ├── check.versions.ts │ │ ├── clean.all.ts │ │ ├── clean.dev.ts │ │ ├── clean.prod.ts │ │ ├── clean.tools.ts │ │ ├── copy.js.prod.ts │ │ ├── css-lint.ts │ │ ├── karma.start.ts │ │ ├── serve.coverage.ts │ │ ├── serve.docs.ts │ │ ├── server.start.ts │ │ ├── tslint.ts │ │ ├── watch.dev.ts │ │ ├── watch.e2e.ts │ │ └── watch.test.ts ├── utils.ts └── utils │ ├── project.utils.ts │ ├── project │ └── sample_util.ts │ ├── seed.utils.ts │ └── seed │ ├── clean.ts │ ├── code_change_tools.ts │ ├── server.ts │ ├── tasks_tools.ts │ ├── template_locals.ts │ ├── tsproject.ts │ └── watch.ts ├── tsconfig.json ├── tslint.json ├── typings.json └── www ├── assets ├── main.css └── svg │ └── more.svg ├── components ├── app.ts ├── login.ts ├── navbar.ts ├── registration.ts ├── tasks.ts └── tasks │ ├── form.ts │ └── list.ts ├── index.html ├── main.ts ├── models └── task.ts ├── pipes └── by_field.ts ├── services ├── auth.ts └── task.ts ├── sw.js └── templates ├── app.html ├── login.html ├── navbar.html ├── registration.html ├── tasks.html └── tasks ├── form.html └── list.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | frontend/build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | frontend/node_modules/ 26 | frontend/typings/ 27 | 28 | # Users Environment Variables 29 | .lock-wscript 30 | .tsdrc 31 | .typingsrc 32 | 33 | #IDE configuration files 34 | .idea 35 | .vscode 36 | *.iml 37 | 38 | frontend/tools/**/*.js 39 | frontend/gulpfile.js 40 | frontend/dist 41 | frontend/dev 42 | frontend/docs 43 | frontend/lib 44 | frontend/test 45 | frontend/tmp 46 | /.bundle 47 | backend/log/* 48 | !backend/log/.keep 49 | backend/tmp 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Task manager Angular2 + rails api 2 | 3 | Setup 4 | - 5 | rake db:create 6 | rake db:migrate 7 | rails s 8 | 9 | Demo 10 | - 11 | [https://task-manager-angular2-rails.herokuapp.com](https://task-manager-angular2-rails.herokuapp.com) 12 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | !/log/.keep 13 | /tmp 14 | -------------------------------------------------------------------------------- /backend/.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /backend/.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.3 3 | Exclude: 4 | - 'vendor/**/*' 5 | - 'db/**/*' 6 | - 'script/**/*' 7 | - 'bin/**/*' 8 | - 'log/**/*' 9 | - 'Gemfile' 10 | - 'spec/support/**/*' 11 | Metrics/LineLength: 12 | Enabled: false 13 | Style/Documentation: 14 | Enabled: false 15 | Style/ClassAndModuleChildren: 16 | Enabled: false 17 | Style/MutableConstant: 18 | Enabled: false 19 | Metrics/ParameterLists: 20 | Enabled: false 21 | -------------------------------------------------------------------------------- /backend/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '4.2.6' 4 | gem 'rails-api' 5 | gem 'pg' 6 | gem 'active_model_serializers', github: 'rails-api/active_model_serializers' 7 | gem 'decent_exposure' 8 | gem 'rack-cors', require: 'rack/cors' 9 | gem 'bcrypt' 10 | gem 'jwt' 11 | 12 | group :development, :test do 13 | gem 'pry' 14 | gem 'spring' 15 | gem 'rubocop' 16 | gem 'rspec-rails' 17 | gem 'shoulda-matchers' 18 | gem 'rspec-collection_matchers' 19 | gem 'factory_girl_rails' 20 | gem 'database_cleaner' 21 | end 22 | -------------------------------------------------------------------------------- /backend/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: git://github.com/rails-api/active_model_serializers.git 3 | revision: c7b2916f37d2feadd46978ee88b568720d152da9 4 | specs: 5 | active_model_serializers (0.10.0.rc4) 6 | actionpack (>= 4.0) 7 | activemodel (>= 4.0) 8 | railties (>= 4.0) 9 | 10 | GEM 11 | remote: https://rubygems.org/ 12 | specs: 13 | actionmailer (4.2.6) 14 | actionpack (= 4.2.6) 15 | actionview (= 4.2.6) 16 | activejob (= 4.2.6) 17 | mail (~> 2.5, >= 2.5.4) 18 | rails-dom-testing (~> 1.0, >= 1.0.5) 19 | actionpack (4.2.6) 20 | actionview (= 4.2.6) 21 | activesupport (= 4.2.6) 22 | rack (~> 1.6) 23 | rack-test (~> 0.6.2) 24 | rails-dom-testing (~> 1.0, >= 1.0.5) 25 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 26 | actionview (4.2.6) 27 | activesupport (= 4.2.6) 28 | builder (~> 3.1) 29 | erubis (~> 2.7.0) 30 | rails-dom-testing (~> 1.0, >= 1.0.5) 31 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 32 | activejob (4.2.6) 33 | activesupport (= 4.2.6) 34 | globalid (>= 0.3.0) 35 | activemodel (4.2.6) 36 | activesupport (= 4.2.6) 37 | builder (~> 3.1) 38 | activerecord (4.2.6) 39 | activemodel (= 4.2.6) 40 | activesupport (= 4.2.6) 41 | arel (~> 6.0) 42 | activesupport (4.2.6) 43 | i18n (~> 0.7) 44 | json (~> 1.7, >= 1.7.7) 45 | minitest (~> 5.1) 46 | thread_safe (~> 0.3, >= 0.3.4) 47 | tzinfo (~> 1.1) 48 | arel (6.0.3) 49 | ast (2.2.0) 50 | bcrypt (3.1.11) 51 | builder (3.2.2) 52 | coderay (1.1.1) 53 | concurrent-ruby (1.0.1) 54 | database_cleaner (1.5.1) 55 | decent_exposure (2.3.3) 56 | diff-lcs (1.2.5) 57 | erubis (2.7.0) 58 | factory_girl (4.5.0) 59 | activesupport (>= 3.0.0) 60 | factory_girl_rails (4.6.0) 61 | factory_girl (~> 4.5.0) 62 | railties (>= 3.0.0) 63 | globalid (0.3.6) 64 | activesupport (>= 4.1.0) 65 | i18n (0.7.0) 66 | json (1.8.3) 67 | jwt (1.5.4) 68 | loofah (2.0.3) 69 | nokogiri (>= 1.5.9) 70 | mail (2.6.4) 71 | mime-types (>= 1.16, < 4) 72 | method_source (0.8.2) 73 | mime-types (3.0) 74 | mime-types-data (~> 3.2015) 75 | mime-types-data (3.2016.0221) 76 | mini_portile2 (2.0.0) 77 | minitest (5.8.4) 78 | nokogiri (1.6.7.2) 79 | mini_portile2 (~> 2.0.0.rc2) 80 | parser (2.3.0.7) 81 | ast (~> 2.2) 82 | pg (0.18.4) 83 | powerpack (0.1.1) 84 | pry (0.10.3) 85 | coderay (~> 1.1.0) 86 | method_source (~> 0.8.1) 87 | slop (~> 3.4) 88 | rack (1.6.4) 89 | rack-cors (0.4.0) 90 | rack-test (0.6.3) 91 | rack (>= 1.0) 92 | rails (4.2.6) 93 | actionmailer (= 4.2.6) 94 | actionpack (= 4.2.6) 95 | actionview (= 4.2.6) 96 | activejob (= 4.2.6) 97 | activemodel (= 4.2.6) 98 | activerecord (= 4.2.6) 99 | activesupport (= 4.2.6) 100 | bundler (>= 1.3.0, < 2.0) 101 | railties (= 4.2.6) 102 | sprockets-rails 103 | rails-api (0.4.0) 104 | actionpack (>= 3.2.11) 105 | railties (>= 3.2.11) 106 | rails-deprecated_sanitizer (1.0.3) 107 | activesupport (>= 4.2.0.alpha) 108 | rails-dom-testing (1.0.7) 109 | activesupport (>= 4.2.0.beta, < 5.0) 110 | nokogiri (~> 1.6.0) 111 | rails-deprecated_sanitizer (>= 1.0.1) 112 | rails-html-sanitizer (1.0.3) 113 | loofah (~> 2.0) 114 | railties (4.2.6) 115 | actionpack (= 4.2.6) 116 | activesupport (= 4.2.6) 117 | rake (>= 0.8.7) 118 | thor (>= 0.18.1, < 2.0) 119 | rainbow (2.1.0) 120 | rake (11.1.2) 121 | rspec-collection_matchers (1.1.2) 122 | rspec-expectations (>= 2.99.0.beta1) 123 | rspec-core (3.4.4) 124 | rspec-support (~> 3.4.0) 125 | rspec-expectations (3.4.0) 126 | diff-lcs (>= 1.2.0, < 2.0) 127 | rspec-support (~> 3.4.0) 128 | rspec-mocks (3.4.1) 129 | diff-lcs (>= 1.2.0, < 2.0) 130 | rspec-support (~> 3.4.0) 131 | rspec-rails (3.4.2) 132 | actionpack (>= 3.0, < 4.3) 133 | activesupport (>= 3.0, < 4.3) 134 | railties (>= 3.0, < 4.3) 135 | rspec-core (~> 3.4.0) 136 | rspec-expectations (~> 3.4.0) 137 | rspec-mocks (~> 3.4.0) 138 | rspec-support (~> 3.4.0) 139 | rspec-support (3.4.1) 140 | rubocop (0.39.0) 141 | parser (>= 2.3.0.7, < 3.0) 142 | powerpack (~> 0.1) 143 | rainbow (>= 1.99.1, < 3.0) 144 | ruby-progressbar (~> 1.7) 145 | unicode-display_width (~> 1.0, >= 1.0.1) 146 | ruby-progressbar (1.7.5) 147 | shoulda-matchers (3.1.1) 148 | activesupport (>= 4.0.0) 149 | slop (3.6.0) 150 | spring (1.6.4) 151 | sprockets (3.5.2) 152 | concurrent-ruby (~> 1.0) 153 | rack (> 1, < 3) 154 | sprockets-rails (3.0.4) 155 | actionpack (>= 4.0) 156 | activesupport (>= 4.0) 157 | sprockets (>= 3.0.0) 158 | thor (0.19.1) 159 | thread_safe (0.3.5) 160 | tzinfo (1.2.2) 161 | thread_safe (~> 0.1) 162 | unicode-display_width (1.0.3) 163 | 164 | PLATFORMS 165 | ruby 166 | 167 | DEPENDENCIES 168 | active_model_serializers! 169 | bcrypt 170 | database_cleaner 171 | decent_exposure 172 | factory_girl_rails 173 | jwt 174 | pg 175 | pry 176 | rack-cors 177 | rails (= 4.2.6) 178 | rails-api 179 | rspec-collection_matchers 180 | rspec-rails 181 | rubocop 182 | shoulda-matchers 183 | spring 184 | 185 | BUNDLED WITH 186 | 1.11.2 187 | -------------------------------------------------------------------------------- /backend/README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | 26 | 27 | Please feel free to use a different markup language if you do not plan to run 28 | rake doc:app. 29 | -------------------------------------------------------------------------------- /backend/Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | Rails.application.load_tasks 8 | -------------------------------------------------------------------------------- /backend/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktor-shmigol/todo-angular2-rails-api/3d74693d00f320787447ed8a7dfe0b8a601e6a07/backend/app/assets/images/.keep -------------------------------------------------------------------------------- /backend/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class ApplicationController < ActionController::API 3 | before_action :authenticate_user! 4 | 5 | decent_configuration do 6 | strategy DecentExposure::StrongParametersStrategy 7 | end 8 | 9 | expose(:current_user) do 10 | token = request.headers['Authorization'].to_s.split(' ').last 11 | User.find_by_token(token) if token 12 | end 13 | 14 | private 15 | 16 | def authenticate_user! 17 | unauthorized! unless current_user 18 | end 19 | 20 | def unauthorized! 21 | head :unauthorized 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /backend/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktor-shmigol/todo-angular2-rails-api/3d74693d00f320787447ed8a7dfe0b8a601e6a07/backend/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /backend/app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class SessionsController < ApplicationController 3 | skip_before_action :authenticate_user! 4 | expose(:user) { User.find_by_email(params[:email]) } 5 | 6 | def create 7 | return render json: { token: user.authenticate! }, status: :ok if user && user.authenticate(params[:password]) 8 | render json: { message: 'Invalid email or password.' }, status: 422 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /backend/app/controllers/tasks_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class TasksController < ApplicationController 3 | expose(:task) 4 | expose(:tasks) { current_user.tasks.order(created_at: :desc) } 5 | 6 | def index 7 | render json: tasks, status: :ok 8 | end 9 | 10 | def create 11 | return render json: task, status: :created if task.update(task_params) 12 | render json: { errors: task.errors.full_messages }, status: 422 13 | end 14 | 15 | def destroy 16 | task.destroy 17 | head(204) 18 | end 19 | 20 | alias update create 21 | 22 | private 23 | 24 | def task_params 25 | params.require(:task).permit(:name, :description, :done).merge(user_id: current_user.id) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /backend/app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class UsersController < ApplicationController 3 | skip_before_action :authenticate_user!, only: :create 4 | expose(:user) 5 | 6 | def create 7 | return render json: { token: user.authenticate! }, status: :created if user.update(user_params) 8 | render json: { status: :error, errors: user.errors.messages }, status: 422 9 | end 10 | 11 | private 12 | 13 | def user_params 14 | params.require(:user).permit(:email, :token, :password, :password_confirmation) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /backend/app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktor-shmigol/todo-angular2-rails-api/3d74693d00f320787447ed8a7dfe0b8a601e6a07/backend/app/mailers/.keep -------------------------------------------------------------------------------- /backend/app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktor-shmigol/todo-angular2-rails-api/3d74693d00f320787447ed8a7dfe0b8a601e6a07/backend/app/models/.keep -------------------------------------------------------------------------------- /backend/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktor-shmigol/todo-angular2-rails-api/3d74693d00f320787447ed8a7dfe0b8a601e6a07/backend/app/models/concerns/.keep -------------------------------------------------------------------------------- /backend/app/models/task.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Task < ActiveRecord::Base 3 | belongs_to :user 4 | validates :name, :description, presence: true 5 | end 6 | -------------------------------------------------------------------------------- /backend/app/models/user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class User < ActiveRecord::Base 3 | has_secure_password 4 | 5 | has_many :tasks, dependent: :delete_all 6 | 7 | validates :password, :password_confirmation, presence: true, confirmation: true, length: { minimum: 8 }, on: :create 8 | validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }, uniqueness: true 9 | 10 | def authenticate! 11 | payload = { data: id } 12 | token = JWT.encode payload, Rails.application.secrets['secret_key_base'], 'HS256' 13 | update_attribute(:token, token) 14 | token 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /backend/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /backend/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../../config/application', __FILE__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /backend/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /backend/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts '== Installing dependencies ==' 12 | system 'gem install bundler --conservative' 13 | system 'bundle check || bundle install' 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system 'bin/rake db:setup' 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system 'rm -f log/*' 25 | system 'rm -rf tmp/cache' 26 | 27 | puts "\n== Restarting application server ==" 28 | system 'touch tmp/restart.txt' 29 | end 30 | -------------------------------------------------------------------------------- /backend/bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) 11 | Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) } 12 | gem 'spring', match[1] 13 | require 'spring/binstub' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /backend/config.ru: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # This file is used by Rack-based servers to start the application. 3 | 4 | require ::File.expand_path('../config/environment', __FILE__) 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /backend/config/application.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require File.expand_path('../boot', __FILE__) 3 | 4 | require 'rails' 5 | # Pick the frameworks you want: 6 | require 'active_model/railtie' 7 | require 'active_job/railtie' 8 | require 'active_record/railtie' 9 | require 'action_controller/railtie' 10 | require 'action_mailer/railtie' 11 | require 'action_view/railtie' 12 | require 'sprockets/railtie' 13 | # require "rails/test_unit/railtie" 14 | 15 | # Require the gems listed in Gemfile, including any gems 16 | # you've limited to :test, :development, or :production. 17 | Bundler.require(*Rails.groups) 18 | 19 | module TaskManager 20 | class Application < Rails::Application 21 | # Settings in config/environments/* take precedence over those specified here. 22 | # Application configuration should go into files in config/initializers 23 | # -- all .rb files in that directory are automatically loaded. 24 | 25 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 26 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 27 | # config.time_zone = 'Central Time (US & Canada)' 28 | 29 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 30 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 31 | # config.i18n.default_locale = :de 32 | 33 | # Do not swallow errors in after_commit/after_rollback callbacks. 34 | config.active_record.raise_in_transactional_callbacks = true 35 | 36 | config.middleware.use Rack::Cors do 37 | allow do 38 | origins '*' 39 | resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /backend/config/boot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | 4 | require 'bundler/setup' # Set up gems listed in the Gemfile. 5 | -------------------------------------------------------------------------------- /backend/config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: postgresql 3 | encoding: unicode 4 | host: localhost 5 | pool: 5 6 | username: postgres 7 | password: 8 | 9 | development: 10 | <<: *default 11 | database: task_manager_development 12 | 13 | test: 14 | <<: *default 15 | database: task_manager_test 16 | -------------------------------------------------------------------------------- /backend/config/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Load the Rails application. 3 | require File.expand_path('../application', __FILE__) 4 | 5 | # Initialize the Rails application. 6 | Rails.application.initialize! 7 | -------------------------------------------------------------------------------- /backend/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Rails.application.configure do 3 | # Settings specified here will take precedence over those in config/application.rb. 4 | 5 | # In the development environment your application's code is reloaded on 6 | # every request. This slows down response time but is perfect for development 7 | # since you don't have to restart the web server when you make code changes. 8 | config.cache_classes = false 9 | 10 | # Do not eager load code on boot. 11 | config.eager_load = false 12 | 13 | # Show full error reports and disable caching. 14 | config.consider_all_requests_local = true 15 | config.action_controller.perform_caching = false 16 | 17 | # Don't care if the mailer can't send. 18 | config.action_mailer.raise_delivery_errors = false 19 | 20 | # Print deprecation notices to the Rails logger. 21 | config.active_support.deprecation = :log 22 | 23 | # Raise an error on page load if there are pending migrations. 24 | config.active_record.migration_error = :page_load 25 | 26 | # Debug mode disables concatenation and preprocessing of assets. 27 | # This option may cause significant delays in view rendering with a large 28 | # number of complex assets. 29 | config.assets.debug = true 30 | 31 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 32 | # yet still be able to expire them through the digest params. 33 | config.assets.digest = true 34 | 35 | # Adds additional error checking when serving assets at runtime. 36 | # Checks for improperly declared sprockets dependencies. 37 | # Raises helpful error messages. 38 | config.assets.raise_runtime_errors = true 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /backend/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Rails.application.configure do 3 | # Settings specified here will take precedence over those in config/application.rb. 4 | 5 | # Code is not reloaded between requests. 6 | config.cache_classes = true 7 | 8 | # Eager load code on boot. This eager loads most of Rails and 9 | # your application in memory, allowing both threaded web servers 10 | # and those relying on copy on write to perform better. 11 | # Rake tasks automatically ignore this option for performance. 12 | config.eager_load = true 13 | 14 | # Full error reports are disabled and caching is turned on. 15 | config.consider_all_requests_local = false 16 | config.action_controller.perform_caching = true 17 | 18 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 19 | # Add `rack-cache` to your Gemfile before enabling this. 20 | # For large-scale production use, consider using a caching reverse proxy like 21 | # NGINX, varnish or squid. 22 | # config.action_dispatch.rack_cache = true 23 | 24 | # Disable serving static files from the `/public` folder by default since 25 | # Apache or NGINX already handles this. 26 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 27 | 28 | # Compress JavaScripts and CSS. 29 | config.assets.js_compressor = :uglifier 30 | # config.assets.css_compressor = :sass 31 | 32 | # Do not fallback to assets pipeline if a precompiled asset is missed. 33 | config.assets.compile = false 34 | 35 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 36 | # yet still be able to expire them through the digest params. 37 | config.assets.digest = true 38 | 39 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 40 | 41 | # Specifies the header that your server uses for sending files. 42 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 43 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 44 | 45 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 46 | # config.force_ssl = true 47 | 48 | # Use the lowest log level to ensure availability of diagnostic information 49 | # when problems arise. 50 | config.log_level = :debug 51 | 52 | # Prepend all log lines with the following tags. 53 | # config.log_tags = [ :subdomain, :uuid ] 54 | 55 | # Use a different logger for distributed setups. 56 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 57 | 58 | # Use a different cache store in production. 59 | # config.cache_store = :mem_cache_store 60 | 61 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 62 | # config.action_controller.asset_host = 'http://assets.example.com' 63 | 64 | # Ignore bad email addresses and do not raise email delivery errors. 65 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 66 | # config.action_mailer.raise_delivery_errors = false 67 | 68 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 69 | # the I18n.default_locale when a translation cannot be found). 70 | config.i18n.fallbacks = true 71 | 72 | # Send deprecation notices to registered listeners. 73 | config.active_support.deprecation = :notify 74 | 75 | # Use default logging formatter so that PID and timestamp are not suppressed. 76 | config.log_formatter = ::Logger::Formatter.new 77 | 78 | # Do not dump schema after migrations. 79 | config.active_record.dump_schema_after_migration = false 80 | end 81 | -------------------------------------------------------------------------------- /backend/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Rails.application.configure do 3 | # Settings specified here will take precedence over those in config/application.rb. 4 | 5 | # The test environment is used exclusively to run your application's 6 | # test suite. You never need to work with it otherwise. Remember that 7 | # your test database is "scratch space" for the test suite and is wiped 8 | # and recreated between test runs. Don't rely on the data there! 9 | config.cache_classes = true 10 | 11 | # Do not eager load code on boot. This avoids loading your whole application 12 | # just for the purpose of running a single test. If you are using a tool that 13 | # preloads Rails for running tests, you may have to set it to true. 14 | config.eager_load = false 15 | 16 | # Configure static file server for tests with Cache-Control for performance. 17 | config.serve_static_files = true 18 | config.static_cache_control = 'public, max-age=3600' 19 | 20 | # Show full error reports and disable caching. 21 | config.consider_all_requests_local = true 22 | config.action_controller.perform_caching = false 23 | 24 | # Raise exceptions instead of rendering exception templates. 25 | config.action_dispatch.show_exceptions = false 26 | 27 | # Disable request forgery protection in test environment. 28 | config.action_controller.allow_forgery_protection = false 29 | 30 | # Tell Action Mailer not to deliver emails to the real world. 31 | # The :test delivery method accumulates sent emails in the 32 | # ActionMailer::Base.deliveries array. 33 | config.action_mailer.delivery_method = :test 34 | 35 | # Randomize the order test cases are executed. 36 | config.active_support.test_order = :random 37 | 38 | # Print deprecation notices to the stderr. 39 | config.active_support.deprecation = :stderr 40 | 41 | # Raises error for missing translations 42 | # config.action_view.raise_on_missing_translations = true 43 | end 44 | -------------------------------------------------------------------------------- /backend/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # Configure sensitive parameters which will be filtered from the log file. 5 | Rails.application.config.filter_parameters += [:password] 6 | -------------------------------------------------------------------------------- /backend/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | # 4 | # This file contains settings for ActionController::ParamsWrapper 5 | 6 | # Enable parameter wrapping for JSON. 7 | # ActiveSupport.on_load(:action_controller) do 8 | # wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | # end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /backend/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /backend/config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Rails.application.routes.draw do 3 | scope 'api' do 4 | resources :users, only: :create 5 | resources :sessions, only: :create 6 | resources :tasks 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /backend/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 52f35482b90081d2ac3f343ff24022e0e3340e4dedd05432707e1cefd2334dace3eca82c51bc15b2ba52135c1544a798cef9103ff738d597eae8f004715439ff 15 | 16 | test: 17 | secret_key_base: e44b4f77b1dc25463d9eaaba16f024cc811b9a5ccd9fe4600ce932fb225dfcaf795e0ff224f86bd07cfc6e687254017f4297e55a88a29bcde4dde19e1a9d7235 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /backend/db/migrate/20160323123002_create_tasks.rb: -------------------------------------------------------------------------------- 1 | class CreateTasks < ActiveRecord::Migration 2 | def change 3 | create_table :tasks do |t| 4 | t.string :name 5 | t.string :description 6 | t.boolean :done, default: false 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /backend/db/migrate/20160324104001_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration 2 | def change 3 | create_table :users do |t| 4 | t.string :email 5 | t.string :token 6 | t.string :password_digest 7 | 8 | t.timestamps null: false 9 | end 10 | add_reference :tasks, :user, index: true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /backend/db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20160324104001) do 15 | 16 | # These are extensions that must be enabled in order to support this database 17 | enable_extension "plpgsql" 18 | 19 | create_table "tasks", force: :cascade do |t| 20 | t.string "name" 21 | t.string "description" 22 | t.boolean "done", default: false 23 | t.datetime "created_at" 24 | t.datetime "updated_at" 25 | t.integer "user_id" 26 | end 27 | 28 | add_index "tasks", ["user_id"], name: "index_tasks_on_user_id", using: :btree 29 | 30 | create_table "users", force: :cascade do |t| 31 | t.string "email" 32 | t.string "token" 33 | t.string "password_digest" 34 | t.datetime "created_at", null: false 35 | t.datetime "updated_at", null: false 36 | end 37 | 38 | end 39 | -------------------------------------------------------------------------------- /backend/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /backend/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktor-shmigol/todo-angular2-rails-api/3d74693d00f320787447ed8a7dfe0b8a601e6a07/backend/lib/assets/.keep -------------------------------------------------------------------------------- /backend/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktor-shmigol/todo-angular2-rails-api/3d74693d00f320787447ed8a7dfe0b8a601e6a07/backend/lib/tasks/.keep -------------------------------------------------------------------------------- /backend/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktor-shmigol/todo-angular2-rails-api/3d74693d00f320787447ed8a7dfe0b8a601e6a07/backend/log/.keep -------------------------------------------------------------------------------- /backend/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /backend/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /backend/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /backend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktor-shmigol/todo-angular2-rails-api/3d74693d00f320787447ed8a7dfe0b8a601e6a07/backend/public/favicon.ico -------------------------------------------------------------------------------- /backend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /backend/spec/controllers/sessions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'rails_helper' 3 | 4 | RSpec.describe SessionsController, type: :controller do 5 | let(:user) { create(:user) } 6 | 7 | context '#create' do 8 | it 'create sessions' do 9 | post :create, email: user.email, password: user.password, format: :json 10 | expect(response).to be_success 11 | expect(json['token']).to_not be_nil 12 | end 13 | 14 | it 'not create session' do 15 | post :create, email: '', format: :json 16 | expect(json['errors']).to be_nil 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /backend/spec/controllers/tasks_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'rails_helper' 3 | 4 | RSpec.describe TasksController, type: :controller do 5 | let!(:user) { create(:user) } 6 | let!(:task) { create(:task, user: user) } 7 | let(:task_atributes) { attributes_for(:task) } 8 | let(:task_invalid_atributes) { attributes_for(:task, name: '') } 9 | 10 | before(:each) do 11 | allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(user) 12 | end 13 | 14 | context '#index' do 15 | it do 16 | get :index 17 | expect(response).to be_success 18 | end 19 | end 20 | 21 | context '#create' do 22 | it 'create task' do 23 | expect { post :create, task: task_atributes }.to change(Task, :count).by(1) 24 | end 25 | 26 | it 'not create task' do 27 | expect { post :create, task: task_invalid_atributes }.to_not change(Task, :count) 28 | end 29 | end 30 | 31 | context '#update' do 32 | it 'update task' do 33 | put :update, id: task.id, task: { name: 'test' } 34 | expect(json['name']).to eq 'test' 35 | end 36 | end 37 | 38 | context '#destroy' do 39 | it do 40 | expect { delete :destroy, id: task.id }.to change(Task, :count).by(-1) 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /backend/spec/controllers/users_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'rails_helper' 3 | 4 | RSpec.describe UsersController, type: :controller do 5 | let!(:user) { create(:user) } 6 | let(:user_atributes) { attributes_for(:user) } 7 | let(:user_invalid_atributes) { attributes_for(:user, email: '') } 8 | 9 | context '#create' do 10 | it 'create user' do 11 | expect { post :create, user: user_atributes, format: :json }.to change(User, :count).by(1) 12 | expect(json['token']).to_not be_nil 13 | end 14 | 15 | it 'not create user' do 16 | expect { post :create, user: user_invalid_atributes, format: :json }.to_not change(User, :count) 17 | expect(json['errors']).to_not be_nil 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /backend/spec/factories/tasks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | FactoryGirl.define do 3 | factory :task do 4 | sequence(:name) { |n| "task#{n}" } 5 | description 'description' 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /backend/spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | FactoryGirl.define do 3 | factory :user do 4 | sequence(:email) { |n| "person#{n}@example.com" } 5 | password 'password' 6 | password_confirmation 'password' 7 | token SecureRandom.urlsafe_base64(nil, false) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /backend/spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | ENV['RAILS_ENV'] ||= 'test' 3 | require File.expand_path('../../config/environment', __FILE__) 4 | abort('The Rails environment is running in production mode!') if Rails.env.production? 5 | require 'spec_helper' 6 | require 'rspec/rails' 7 | require 'database_cleaner' 8 | require 'rspec/collection_matchers' 9 | 10 | ActiveRecord::Migration.maintain_test_schema! 11 | 12 | RSpec.configure do |config| 13 | Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } 14 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 15 | config.include FactoryGirl::Syntax::Methods 16 | config.extend ControllerMacros, type: :controller 17 | config.include Requests::JsonHelpers, type: :controller 18 | config.use_transactional_fixtures = true 19 | config.infer_spec_type_from_file_location! 20 | config.filter_rails_from_backtrace! 21 | 22 | config.before(:suite) do 23 | DatabaseCleaner.clean_with(:truncation) 24 | end 25 | 26 | config.before(:each) do 27 | DatabaseCleaner.strategy = :transaction 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /backend/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | RSpec.configure do |config| 3 | config.expect_with :rspec do |expectations| 4 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 5 | end 6 | 7 | config.mock_with :rspec do |mocks| 8 | mocks.verify_partial_doubles = true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /backend/spec/support/controller_macros.rb: -------------------------------------------------------------------------------- 1 | module ControllerMacros 2 | def login_as_user 3 | before(:each) do 4 | @user = create(:user) 5 | allow_any_instance_of(V1::BaseController).to receive(:current_user).and_return(@user) 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /backend/spec/support/request_helpers.rb: -------------------------------------------------------------------------------- 1 | module Requests 2 | module JsonHelpers 3 | def json 4 | @json ||= JSON.parse(response.body) 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /frontend/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "es3": false, 7 | "forin": true, 8 | "freeze": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": "nofunc", 12 | "newcap": true, 13 | "noarg": true, 14 | "noempty": true, 15 | "nonbsp": true, 16 | "nonew": true, 17 | "plusplus": false, 18 | "quotmark": "single", 19 | "undef": true, 20 | "unused": false, 21 | "strict": false, 22 | "maxparams": 10, 23 | "maxdepth": 5, 24 | "maxstatements": 40, 25 | "maxcomplexity": 8, 26 | "maxlen": 140, 27 | 28 | "asi": false, 29 | "boss": false, 30 | "debug": false, 31 | "eqnull": true, 32 | "esnext": false, 33 | "evil": false, 34 | "expr": false, 35 | "funcscope": false, 36 | "globalstrict": false, 37 | "iterator": false, 38 | "lastsemic": false, 39 | "laxbreak": false, 40 | "laxcomma": false, 41 | "loopfunc": true, 42 | "maxerr": false, 43 | "moz": false, 44 | "multistr": false, 45 | "notypeof": false, 46 | "proto": false, 47 | "scripturl": false, 48 | "shadow": false, 49 | "sub": true, 50 | "supernew": false, 51 | "validthis": false, 52 | "noyield": false, 53 | 54 | "browser": true, 55 | "node": true, 56 | 57 | "globals": { 58 | "angular": false, 59 | "ng": false 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /frontend/.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "rules": { 4 | "block-no-empty": null, 5 | "at-rule-empty-line-before": null, 6 | "rule-non-nested-empty-line-before": null 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /frontend/Procfile: -------------------------------------------------------------------------------- 1 | web: node app.js 2 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | npm install 2 | npm start 3 | -------------------------------------------------------------------------------- /frontend/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | var request = require('request'); 4 | 5 | app.use('/api', function(req, res) { 6 | var url = 'https://task-manager-api.herokuapp.com/api/' + req.url; 7 | req.pipe(request(url)).pipe(res); 8 | }); 9 | app.use(express.static(__dirname + '/dist/prod')); 10 | app.get('/*', function(req, res){ 11 | res.sendFile(__dirname + '/dist/prod/index.html'); 12 | }); 13 | app.listen(process.env.PORT || 3000); 14 | -------------------------------------------------------------------------------- /frontend/gulpfile.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as runSequence from 'run-sequence'; 3 | import {loadTasks} from './tools/utils'; 4 | import {SEED_TASKS_DIR, PROJECT_TASKS_DIR} from './tools/config'; 5 | 6 | loadTasks(SEED_TASKS_DIR); 7 | loadTasks(PROJECT_TASKS_DIR); 8 | 9 | 10 | // -------------- 11 | // Build dev. 12 | gulp.task('build.dev', (done: any) => 13 | runSequence('clean.dev', 14 | 'tslint', 15 | 'css-lint', 16 | 'build.assets.dev', 17 | 'build.html_css', 18 | 'build.js.dev', 19 | 'build.index.dev', 20 | done)); 21 | 22 | // -------------- 23 | // Build dev watch. 24 | gulp.task('build.dev.watch', (done: any) => 25 | runSequence('build.dev', 26 | 'watch.dev', 27 | done)); 28 | 29 | // -------------- 30 | // Build e2e. 31 | gulp.task('build.e2e', (done: any) => 32 | runSequence('clean.dev', 33 | 'tslint', 34 | 'build.assets.dev', 35 | 'build.js.e2e', 36 | 'build.index.dev', 37 | done)); 38 | 39 | // -------------- 40 | // Build prod. 41 | gulp.task('build.prod', (done: any) => 42 | runSequence('clean.prod', 43 | 'tslint', 44 | 'css-lint', 45 | 'build.assets.prod', 46 | 'build.html_css', 47 | 'copy.js.prod', 48 | 'build.js.prod', 49 | 'build.bundles', 50 | 'build.bundles.app', 51 | 'build.index.prod', 52 | done)); 53 | 54 | // -------------- 55 | // Build test. 56 | gulp.task('build.test', (done: any) => 57 | runSequence('clean.dev', 58 | 'tslint', 59 | 'build.assets.dev', 60 | 'build.js.test', 61 | 'build.index.dev', 62 | done)); 63 | 64 | // -------------- 65 | // Build test watch. 66 | gulp.task('build.test.watch', (done: any) => 67 | runSequence('build.test', 68 | 'watch.test', 69 | done)); 70 | 71 | // -------------- 72 | // Build tools. 73 | gulp.task('build.tools', (done: any) => 74 | runSequence('clean.tools', 75 | 'build.js.tools', 76 | done)); 77 | 78 | // -------------- 79 | // Docs 80 | gulp.task('docs', (done: any) => 81 | runSequence('build.docs', 82 | 'serve.docs', 83 | done)); 84 | 85 | // -------------- 86 | // Serve dev 87 | gulp.task('serve.dev', (done: any) => 88 | runSequence('build.dev', 89 | 'server.start', 90 | 'watch.dev', 91 | done)); 92 | 93 | // -------------- 94 | // Serve e2e 95 | gulp.task('serve.e2e', (done: any) => 96 | runSequence('build.e2e', 97 | 'server.start', 98 | 'watch.e2e', 99 | done)); 100 | 101 | // -------------- 102 | // Test. 103 | gulp.task('test', (done: any) => 104 | runSequence('build.test', 105 | 'karma.start', 106 | done)); 107 | -------------------------------------------------------------------------------- /frontend/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Wed Jul 15 2015 09:44:02 GMT+0200 (Romance Daylight Time) 3 | 'use strict'; 4 | 5 | var argv = require('yargs').argv; 6 | 7 | module.exports = function(config) { 8 | config.set({ 9 | 10 | // base path that will be used to resolve all patterns (eg. files, exclude) 11 | basePath: './', 12 | 13 | 14 | // frameworks to use 15 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 16 | frameworks: ['jasmine'], 17 | 18 | 19 | // list of files / patterns to load in the browser 20 | files: [ 21 | 'node_modules/zone.js/dist/zone-microtask.js', 22 | 'node_modules/zone.js/dist/long-stack-trace-zone.js', 23 | 'node_modules/zone.js/dist/jasmine-patch.js', 24 | 'node_modules/es6-module-loader/dist/es6-module-loader.js', 25 | 'node_modules/traceur/bin/traceur-runtime.js', // Required by PhantomJS2, otherwise it shouts ReferenceError: Can't find variable: require 26 | 'node_modules/traceur/bin/traceur.js', 27 | 'node_modules/systemjs/dist/system.src.js', 28 | 'node_modules/reflect-metadata/Reflect.js', 29 | // beta.7 IE 11 polyfills from https://github.com/angular/angular/issues/7144 30 | 'node_modules/angular2/es6/dev/src/testing/shims_for_IE.js', 31 | 32 | { pattern: 'node_modules/angular2/**/*.js', included: false, watched: false }, 33 | { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, 34 | { pattern: 'dist/dev/**/*.js', included: false, watched: true }, 35 | { pattern: 'node_modules/systemjs/dist/system-polyfills.js', included: false, watched: false }, // PhantomJS2 (and possibly others) might require it 36 | 37 | 'test-main.js' 38 | ], 39 | 40 | 41 | // list of files to exclude 42 | exclude: [ 43 | 'node_modules/angular2/**/*spec.js' 44 | ], 45 | 46 | 47 | // preprocess matching files before serving them to the browser 48 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 49 | preprocessors: { 50 | 'dist/**/!(*spec).js': ['coverage'] 51 | }, 52 | 53 | // test results reporter to use 54 | // possible values: 'dots', 'progress' 55 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 56 | reporters: ['mocha', 'coverage'], 57 | 58 | 59 | // web server port 60 | port: 9876, 61 | 62 | 63 | // enable / disable colors in the output (reporters and logs) 64 | colors: true, 65 | 66 | 67 | // level of logging 68 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 69 | logLevel: config.LOG_INFO, 70 | 71 | 72 | // enable / disable watching file and executing tests whenever any file changes 73 | autoWatch: true, 74 | 75 | 76 | // start these browsers 77 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 78 | browsers: [ 79 | 'PhantomJS', 80 | 'Chrome' 81 | ], 82 | 83 | 84 | customLaunchers: { 85 | Chrome_travis_ci: { 86 | base: 'Chrome', 87 | flags: ['--no-sandbox'] 88 | } 89 | }, 90 | 91 | coverageReporter: { 92 | dir: 'coverage/', 93 | reporters: [ 94 | { type: 'text-summary' }, 95 | { type: 'json', subdir: '.', file: 'coverage-final.json' }, 96 | { type: 'html' } 97 | ] 98 | }, 99 | 100 | // Continuous Integration mode 101 | // if true, Karma captures browsers, runs the tests and exits 102 | singleRun: false, 103 | 104 | // Passing command line arguments to tests 105 | client: { 106 | files: argv.files 107 | } 108 | }); 109 | 110 | if (process.env.APPVEYOR) { 111 | config.browsers = ['IE']; 112 | config.singleRun = true; 113 | config.browserNoActivityTimeout = 90000; // Note: default value (10000) is not enough 114 | } 115 | 116 | if (process.env.TRAVIS || process.env.CIRCLECI) { 117 | config.browsers = ['Chrome_travis_ci']; 118 | config.singleRun = true; 119 | } 120 | }; 121 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2-seed", 3 | "version": "0.0.0", 4 | "description": "Seed for Angular 2 apps", 5 | "repository": { 6 | "url": "https://github.com/mgechev/angular2-seed" 7 | }, 8 | "scripts": { 9 | "build.dev": "gulp build.dev", 10 | "build.dev.watch": "gulp build.dev.watch", 11 | "build.e2e": "gulp build.e2e", 12 | "build.prod": "gulp build.prod", 13 | "build.test": "gulp build.test", 14 | "build.test.watch": "gulp build.test.watch", 15 | "docs": "npm run gulp -- build.docs && npm run gulp -- serve.docs", 16 | "e2e": "protractor", 17 | "e2e.live": "protractor --elementExplorer", 18 | "gulp": "gulp", 19 | "karma": "karma", 20 | "karma.start": "karma start", 21 | "postinstall": "typings install && gulp check.versions && npm prune", 22 | "reinstall": "npm cache clean && npm install", 23 | "serve.coverage": "remap-istanbul -b src/ -i coverage/coverage-final.json -o coverage -t html && npm run gulp -- serve.coverage", 24 | "serve.dev": "gulp serve.dev", 25 | "serve.e2e": "gulp serve.e2e", 26 | "start": "gulp serve.dev", 27 | "tasks.list": "gulp --tasks-simple", 28 | "test": "gulp test", 29 | "webdriver-start": "webdriver-manager start", 30 | "webdriver-update": "webdriver-manager update" 31 | }, 32 | "author": "Minko Gechev ", 33 | "license": "MIT", 34 | "devDependencies": { 35 | "async": "^1.4.2", 36 | "autoprefixer": "^6.3.3", 37 | "browser-sync": "~2.10.1", 38 | "chalk": "^1.1.1", 39 | "colorguard": "^1.0.1", 40 | "connect": "^3.4.1", 41 | "connect-history-api-fallback": "^1.1.0", 42 | "connect-livereload": "^0.5.3", 43 | "cssnano": "^3.5.2", 44 | "doiuse": "^2.3.0", 45 | "event-stream": "^3.3.2", 46 | "express": "~4.13.1", 47 | "extend": "^3.0.0", 48 | "gulp": "^3.9.1", 49 | "gulp-cached": "^1.1.0", 50 | "gulp-concat": "^2.6.0", 51 | "gulp-filter": "^4.0.0", 52 | "gulp-inject": "^4.0.0", 53 | "gulp-inline-ng2-template": "^1.1.2", 54 | "gulp-load-plugins": "^1.2.0", 55 | "gulp-plumber": "~1.1.0", 56 | "gulp-postcss": "^6.1.0", 57 | "gulp-shell": "~0.5.2", 58 | "gulp-sourcemaps": "git+https://github.com/floridoo/gulp-sourcemaps.git#master", 59 | "gulp-template": "^3.1.0", 60 | "gulp-tslint": "^4.3.3", 61 | "gulp-tslint-stylish": "^1.1.1", 62 | "gulp-typedoc": "^1.2.1", 63 | "gulp-typescript": "~2.12.1", 64 | "gulp-uglify": "^1.5.3", 65 | "gulp-util": "^3.0.7", 66 | "gulp-watch": "^4.3.5", 67 | "isstream": "^0.1.2", 68 | "jasmine-core": "~2.4.1", 69 | "jasmine-spec-reporter": "^2.4.0", 70 | "karma": "~0.13.22", 71 | "karma-chrome-launcher": "~0.2.2", 72 | "karma-coverage": "^0.5.5", 73 | "karma-ie-launcher": "^0.2.0", 74 | "karma-jasmine": "~0.3.8", 75 | "karma-mocha-reporter": "^2.0.0", 76 | "karma-phantomjs-launcher": "^1.0.0", 77 | "merge-stream": "^1.0.0", 78 | "ng2lint": "0.0.10", 79 | "open": "0.0.5", 80 | "phantomjs-prebuilt": "^2.1.4", 81 | "postcss-reporter": "^1.3.3", 82 | "protractor": "^3.0.0", 83 | "proxy-middleware": "^0.15.0", 84 | "remap-istanbul": "git+https://github.com/SitePen/remap-istanbul.git#master", 85 | "rimraf": "^2.5.2", 86 | "run-sequence": "^1.1.0", 87 | "semver": "^5.1.0", 88 | "serve-static": "^1.10.2", 89 | "slash": "~1.0.0", 90 | "stream-series": "^0.1.1", 91 | "stylelint": "^5.1.0", 92 | "stylelint-config-standard": "^4.0.1", 93 | "systemjs-builder": "^0.15.13", 94 | "tiny-lr": "^0.2.1", 95 | "traceur": "^0.0.91", 96 | "ts-node": "^0.6.2", 97 | "tslint": "^3.6.0", 98 | "typedoc": "^0.3.12", 99 | "typescript": "~1.8.7", 100 | "typings": "^0.7.9", 101 | "vinyl-buffer": "^1.0.0", 102 | "vinyl-source-stream": "^1.1.0", 103 | "yargs": "^4.2.0" 104 | }, 105 | "dependencies": { 106 | "angular2": "2.0.0-beta.9", 107 | "angular2-jwt": "^0.1.8", 108 | "bower": "^1.7.7", 109 | "es6-module-loader": "^0.17.8", 110 | "es6-promise": "^3.1.2", 111 | "es6-shim": "0.33.3", 112 | "ng2-bootstrap": "^1.0.7", 113 | "reflect-metadata": "0.1.2", 114 | "rxjs": "5.0.0-beta.2", 115 | "systemjs": "~0.19.18", 116 | "zone.js": "0.5.15", 117 | "express": "^4.13.4", 118 | "request": "^2.69.0" 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /frontend/protractor.conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | baseUrl: 'http://localhost:5555', 3 | 4 | specs: [ 5 | 'dist/dev/**/*.e2e.js' 6 | ], 7 | exclude: [], 8 | 9 | framework: 'jasmine2', 10 | 11 | allScriptsTimeout: 110000, 12 | 13 | jasmineNodeOpts: { 14 | showTiming: true, 15 | showColors: true, 16 | isVerbose: false, 17 | includeStackTrace: false, 18 | defaultTimeoutInterval: 400000 19 | }, 20 | directConnect: true, 21 | 22 | capabilities: { 23 | 'browserName': 'chrome' 24 | }, 25 | 26 | onPrepare: function() { 27 | var SpecReporter = require('jasmine-spec-reporter'); 28 | // add jasmine spec reporter 29 | jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: true})); 30 | 31 | browser.ignoreSynchronization = false; 32 | }, 33 | 34 | 35 | /** 36 | * Angular 2 configuration 37 | * 38 | * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching 39 | * `rootEl` 40 | * 41 | */ 42 | useAllAngular2AppRoots: true 43 | }; 44 | -------------------------------------------------------------------------------- /frontend/test-main.js: -------------------------------------------------------------------------------- 1 | if (!Object.hasOwnProperty('name')) { 2 | Object.defineProperty(Function.prototype, 'name', { 3 | get: function() { 4 | var matches = this.toString().match(/^\s*function\s*(\S*)\s*\(/); 5 | var name = matches && matches.length > 1 ? matches[1] : ""; 6 | Object.defineProperty(this, 'name', {value: name}); 7 | return name; 8 | } 9 | }); 10 | } 11 | 12 | // Turn on full stack traces in errors to help debugging 13 | Error.stackTraceLimit = Infinity; 14 | 15 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 16 | 17 | // Cancel Karma's synchronous start, 18 | // we will call `__karma__.start()` later, once all the specs are loaded. 19 | __karma__.loaded = function() {}; 20 | 21 | System.config({ 22 | baseURL: '/base/', 23 | defaultJSExtensions: true, 24 | paths: { 25 | 'angular2/*': 'node_modules/angular2/*.js', 26 | 'rxjs/*': 'node_modules/rxjs/*.js' 27 | } 28 | }); 29 | 30 | Promise.all([ 31 | System.import('angular2/src/platform/browser/browser_adapter'), 32 | System.import('angular2/platform/testing/browser'), 33 | System.import('angular2/testing') 34 | ]).then(function (modules) { 35 | var browser_adapter = modules[0]; 36 | var providers = modules[1]; 37 | var testing = modules[2]; 38 | testing.setBaseTestProviders(providers.TEST_BROWSER_PLATFORM_PROVIDERS, 39 | providers.TEST_BROWSER_APPLICATION_PROVIDERS); 40 | 41 | browser_adapter.BrowserDomAdapter.makeCurrent(); 42 | }).then(function() { 43 | return Promise.all( 44 | Object.keys(window.__karma__.files) // All files served by Karma. 45 | .filter(onlySpecFiles) 46 | .map(file2moduleName) 47 | .map(function(path) { 48 | return System.import(path).then(function(module) { 49 | if (module.hasOwnProperty('main')) { 50 | module.main(); 51 | } else { 52 | throw new Error('Module ' + path + ' does not implement main() method.'); 53 | } 54 | }); 55 | })); 56 | }) 57 | .then(function() { 58 | __karma__.start(); 59 | }, function(error) { 60 | console.error(error.stack || error); 61 | __karma__.start(); 62 | }); 63 | 64 | function onlySpecFiles(path) { 65 | // check for individual files, if not given, always matches to all 66 | var patternMatched = __karma__.config.files ? 67 | path.match(new RegExp(__karma__.config.files)) : true; 68 | 69 | return patternMatched && /[\.|_]spec\.js$/.test(path); 70 | } 71 | 72 | // Normalize paths to module names. 73 | function file2moduleName(filePath) { 74 | return filePath.replace(/\\/g, '/') 75 | .replace(/^\/base\//, '') 76 | .replace(/\.js/, ''); 77 | } 78 | -------------------------------------------------------------------------------- /frontend/tools/README.md: -------------------------------------------------------------------------------- 1 | # Tools documentation 2 | 3 | ## Configuration 4 | 5 | ## Tasks 6 | 7 | ## Utilities 8 | 9 | -------------------------------------------------------------------------------- /frontend/tools/config.ts: -------------------------------------------------------------------------------- 1 | import {ProjectConfig} from './config/project.config'; 2 | 3 | const config: ProjectConfig = new ProjectConfig(); 4 | export = config; 5 | -------------------------------------------------------------------------------- /frontend/tools/config/project.config.ts: -------------------------------------------------------------------------------- 1 | import {join} from 'path'; 2 | import {SeedConfig} from './seed.config'; 3 | import {InjectableDependency} from './seed.config.interfaces'; 4 | 5 | export class ProjectConfig extends SeedConfig { 6 | PROJECT_TASKS_DIR = join(process.cwd(), this.TOOLS_DIR, 'tasks', 'project'); 7 | 8 | constructor() { 9 | super(); 10 | // this.APP_TITLE = 'Put name of your app here'; 11 | let additional_deps: InjectableDependency[] = [ 12 | // {src: 'jquery/dist/jquery.min.js', inject: 'libs'}, 13 | // {src: 'lodash/lodash.min.js', inject: 'libs'}, 14 | ]; 15 | 16 | const seedDependencies = this.NPM_DEPENDENCIES; 17 | 18 | this.NPM_DEPENDENCIES = seedDependencies.concat(additional_deps); 19 | 20 | this.APP_ASSETS = [ 21 | // {src: `${this.ASSETS_SRC}/css/toastr.min.css`, inject: true}, 22 | // {src: `${this.APP_DEST}/assets/scss/global.css`, inject: true}, 23 | { src: `${this.ASSETS_SRC}/main.css`, inject: true }, 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/tools/config/seed.config.interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface InjectableDependency { 2 | src: string; 3 | inject: string | boolean; 4 | vendor?: boolean; 5 | env?: string[] | string; 6 | } 7 | 8 | export interface Environments { 9 | DEVELOPMENT: string; 10 | PRODUCTION: string; 11 | [key: string]: string; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /frontend/tools/config/seed.config.ts: -------------------------------------------------------------------------------- 1 | import {readFileSync} from 'fs'; 2 | import {argv} from 'yargs'; 3 | import {normalize, join} from 'path'; 4 | import {InjectableDependency, Environments} from './seed.config.interfaces'; 5 | 6 | export const ENVIRONMENTS: Environments = { 7 | DEVELOPMENT: 'dev', 8 | PRODUCTION: 'prod' 9 | }; 10 | 11 | 12 | export class SeedConfig { 13 | PORT = argv['port'] || 8100; 14 | PROJECT_ROOT = normalize(join(__dirname, '..')); 15 | ENV = getEnvironment(); 16 | DEBUG = argv['debug'] || false; 17 | DOCS_PORT = argv['docs-port'] || 4003; 18 | COVERAGE_PORT = argv['coverage-port'] || 4004; 19 | APP_BASE = argv['base'] || '/'; 20 | 21 | ENABLE_HOT_LOADING = argv['hot-loader']; 22 | HOT_LOADER_PORT = 5578; 23 | 24 | BOOTSTRAP_MODULE = this.ENABLE_HOT_LOADING ? 'hot_loader_main' : 'main'; 25 | 26 | APP_TITLE = 'Task manager'; 27 | 28 | APP_SRC = 'www'; 29 | ASSETS_SRC = `${this.APP_SRC}/assets`; 30 | 31 | TOOLS_DIR = 'tools'; 32 | SEED_TASKS_DIR = join(process.cwd(), this.TOOLS_DIR, 'tasks', 'seed'); 33 | DOCS_DEST = 'docs'; 34 | DIST_DIR = 'dist'; 35 | DEV_DEST = `${this.DIST_DIR}/dev`; 36 | PROD_DEST = `${this.DIST_DIR}/prod`; 37 | TMP_DIR = `${this.DIST_DIR}/tmp`; 38 | APP_DEST = `${this.DIST_DIR}/${this.ENV}`; 39 | CSS_DEST = `${this.APP_DEST}/css`; 40 | JS_DEST = `${this.APP_DEST}/js`; 41 | APP_ROOT = this.ENV === 'dev' ? `${this.APP_BASE}${this.APP_DEST}/` : `${this.APP_BASE}`; 42 | VERSION = appVersion(); 43 | 44 | CSS_PROD_BUNDLE = 'all.css'; 45 | JS_PROD_SHIMS_BUNDLE = 'shims.js'; 46 | JS_PROD_APP_BUNDLE = 'app.js'; 47 | 48 | VERSION_NPM = '2.14.2'; 49 | VERSION_NODE = '4.0.0'; 50 | 51 | NG2LINT_RULES = customRules(); 52 | 53 | NPM_DEPENDENCIES: InjectableDependency[] = [ 54 | { src: 'systemjs/dist/system-polyfills.src.js', inject: 'shims' }, 55 | { src: 'reflect-metadata/Reflect.js', inject: 'shims' }, 56 | { src: 'es6-shim/es6-shim.js', inject: 'shims' }, 57 | { src: 'systemjs/dist/system.src.js', inject: 'shims' }, 58 | { src: 'angular2/bundles/angular2-polyfills.js', inject: 'shims' }, 59 | { src: 'rxjs/bundles/Rx.js', inject: 'libs', env: ENVIRONMENTS.DEVELOPMENT }, 60 | { src: 'angular2/bundles/angular2.js', inject: 'libs', env: ENVIRONMENTS.DEVELOPMENT }, 61 | { src: 'angular2/bundles/router.js', inject: 'libs', env: ENVIRONMENTS.DEVELOPMENT }, 62 | { src: 'angular2/bundles/http.js', inject: 'libs', env: ENVIRONMENTS.DEVELOPMENT } 63 | ]; 64 | 65 | // Declare local files that needs to be injected 66 | APP_ASSETS: InjectableDependency[] = [ 67 | { src: `${this.ASSETS_SRC}/main.css`, inject: true, vendor: false } 68 | ]; 69 | 70 | 71 | get PROD_DEPENDENCIES(): InjectableDependency[] { 72 | console.warn('The property "PROD_DEPENDENCIES" is deprecated. Consider using "DEPENDENCIES" instead.'); 73 | return normalizeDependencies(this.NPM_DEPENDENCIES.filter(filterDependency.bind(null, ENVIRONMENTS.PRODUCTION))) 74 | .concat(this.APP_ASSETS.filter(filterDependency.bind(null, ENVIRONMENTS.PRODUCTION))); 75 | } 76 | 77 | get DEV_DEPENDENCIES(): InjectableDependency[] { 78 | console.warn('The property "DEV_DEPENDENCIES" is deprecated. Consider using "DEPENDENCIES" instead.'); 79 | return normalizeDependencies(this.NPM_DEPENDENCIES.filter(filterDependency.bind(null, ENVIRONMENTS.DEVELOPMENT))) 80 | .concat(this.APP_ASSETS.filter(filterDependency.bind(null, ENVIRONMENTS.DEVELOPMENT))); 81 | } 82 | 83 | set DEV_DEPENDENCIES(val: InjectableDependency[]) { 84 | console.warn('The property "DEV_DEPENDENCIES" is deprecated. Consider using "DEPENDENCIES" instead.'); 85 | } 86 | 87 | set PROD_DEPENDENCIES(val: InjectableDependency[]) { 88 | console.warn('The property "PROD_DEPENDENCIES" is deprecated. Consider using "DEPENDENCIES" instead.'); 89 | } 90 | 91 | get DEV_NPM_DEPENDENCIES(): InjectableDependency[] { 92 | console.warn('The property "DEV_NPM_DEPENDENCIES" is deprecated. Consider using "DEPENDENCIES" instead.'); 93 | return normalizeDependencies(this.NPM_DEPENDENCIES.filter(filterDependency.bind(null, ENVIRONMENTS.DEVELOPMENT))); 94 | } 95 | get PROD_NPM_DEPENDENCIES(): InjectableDependency[] { 96 | console.warn('The property "PROD_NPM_DEPENDENCIES" is deprecated. Consider using "DEPENDENCIES" instead.'); 97 | return normalizeDependencies(this.NPM_DEPENDENCIES.filter(filterDependency.bind(null, ENVIRONMENTS.PRODUCTION))); 98 | } 99 | set DEV_NPM_DEPENDENCIES(value: InjectableDependency[]) { 100 | console.warn('The property "DEV_NPM_DEPENDENCIES" is deprecated. Consider using "DEPENDENCIES" instead.'); 101 | const notDev = this.NPM_DEPENDENCIES.filter(d => !filterDependency(ENVIRONMENTS.DEVELOPMENT, d)); 102 | this.NPM_DEPENDENCIES = notDev.concat(value); 103 | } 104 | set PROD_NPM_DEPENDENCIES(value: InjectableDependency[]) { 105 | console.warn('The property "PROD_NPM_DEPENDENCIES" is deprecated. Consider using "DEPENDENCIES" instead.'); 106 | const notProd = this.NPM_DEPENDENCIES.filter(d => !filterDependency(ENVIRONMENTS.PRODUCTION, d)); 107 | this.NPM_DEPENDENCIES = notProd.concat(value); 108 | } 109 | 110 | get DEPENDENCIES(): InjectableDependency[] { 111 | return normalizeDependencies(this.NPM_DEPENDENCIES.filter(filterDependency.bind(null, this.ENV))) 112 | .concat(this.APP_ASSETS.filter(filterDependency.bind(null, this.ENV))); 113 | } 114 | 115 | // ---------------- 116 | // SystemsJS Configuration. 117 | protected SYSTEM_CONFIG_DEV = { 118 | defaultJSExtensions: true, 119 | packageConfigPaths: [`${this.APP_BASE}node_modules/*/package.json`], 120 | paths: { 121 | [this.BOOTSTRAP_MODULE]: `${this.APP_BASE}${this.BOOTSTRAP_MODULE}`, 122 | 'angular2/*': `${this.APP_BASE}angular2/*`, 123 | 'rxjs/*': `${this.APP_BASE}rxjs/*`, 124 | '*': `${this.APP_BASE}node_modules/*` 125 | }, 126 | packages: { 127 | angular2: { defaultExtension: false }, 128 | rxjs: { defaultExtension: false } 129 | } 130 | }; 131 | 132 | SYSTEM_CONFIG = this.SYSTEM_CONFIG_DEV; 133 | 134 | SYSTEM_BUILDER_CONFIG = { 135 | defaultJSExtensions: true, 136 | paths: { 137 | [`${this.TMP_DIR}/*`]: `${this.TMP_DIR}/*`, 138 | '*': 'node_modules/*' 139 | } 140 | }; 141 | 142 | // ---------------- 143 | // Autoprefixer configuration. 144 | BROWSER_LIST = [ 145 | 'ie >= 10', 146 | 'ie_mob >= 10', 147 | 'ff >= 30', 148 | 'chrome >= 34', 149 | 'safari >= 7', 150 | 'opera >= 23', 151 | 'ios >= 7', 152 | 'android >= 4.4', 153 | 'bb >= 10' 154 | ]; 155 | getEnvDependencies() { 156 | console.warn('The "getEnvDependencies" method is deprecated. Consider using "DEPENDENCIES" instead.'); 157 | if (this.ENV === 'prod') { 158 | return this.PROD_DEPENDENCIES; 159 | } else { 160 | return this.DEV_DEPENDENCIES; 161 | } 162 | } 163 | } 164 | 165 | 166 | 167 | 168 | // -------------- 169 | // Utils. 170 | 171 | function filterDependency(env: string, d: InjectableDependency): boolean { 172 | if (!d.env) { 173 | d.env = Object.keys(ENVIRONMENTS).map(k => ENVIRONMENTS[k]); 174 | } 175 | if (!(d.env instanceof Array)) { 176 | (d).env = [d.env]; 177 | } 178 | return d.env.indexOf(env) >= 0; 179 | } 180 | 181 | export function normalizeDependencies(deps: InjectableDependency[]) { 182 | deps 183 | .filter((d:InjectableDependency) => !/\*/.test(d.src)) // Skip globs 184 | .forEach((d:InjectableDependency) => d.src = require.resolve(d.src)); 185 | return deps; 186 | } 187 | 188 | function appVersion(): number|string { 189 | var pkg = JSON.parse(readFileSync('package.json').toString()); 190 | return pkg.version; 191 | } 192 | 193 | function customRules(): string[] { 194 | var lintConf = JSON.parse(readFileSync('tslint.json').toString()); 195 | return lintConf.rulesDirectory; 196 | } 197 | 198 | function getEnvironment() { 199 | let base:string[] = argv['_']; 200 | let prodKeyword = !!base.filter(o => o.indexOf(ENVIRONMENTS.PRODUCTION) >= 0).pop(); 201 | if (base && prodKeyword || argv['env'] === ENVIRONMENTS.PRODUCTION) { 202 | return ENVIRONMENTS.PRODUCTION; 203 | } else { 204 | return ENVIRONMENTS.DEVELOPMENT; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /frontend/tools/debug.ts: -------------------------------------------------------------------------------- 1 | import {argv} from 'yargs'; 2 | import * as gulp from 'gulp'; 3 | 4 | require('../gulpfile'); 5 | 6 | const TASK = argv['task']; 7 | 8 | if (!TASK) { 9 | throw new Error('You must specify a task name.'); 10 | } 11 | 12 | console.log('**********************'); 13 | console.log('* angular2-seed tools '); 14 | console.log('* debugging task:', TASK); 15 | console.log('**********************'); 16 | 17 | gulp.start(TASK); 18 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/project/sample.package.d.ts: -------------------------------------------------------------------------------- 1 | // declare module "moment/moment" { 2 | // export = moment; 3 | // } 4 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/angular2-hot-loader.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'angular2-hot-loader' { 2 | export interface Options { 3 | port?: number; 4 | path?: string; 5 | processPath?: Function; 6 | } 7 | export function listen(localConfig?: Options): void; 8 | export function onChange(files: string[]): void; 9 | } 10 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/autoprefixer.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'autoprefixer' { 2 | 3 | interface IOptions { 4 | browsers: string[]; 5 | } 6 | 7 | interface IAutoprefixer { 8 | (opts?: IOptions): NodeJS.ReadWriteStream; 9 | } 10 | 11 | const autoprefixer: IAutoprefixer; 12 | export = autoprefixer; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/colorguard.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'colorguard' { 2 | 3 | interface IOptions { 4 | ignore?: string[]; 5 | threshold?: number; 6 | whitelist?: string[]; 7 | } 8 | 9 | interface IColorguard { 10 | (opts?: IOptions): NodeJS.ReadWriteStream; 11 | } 12 | 13 | const colorguard: IColorguard; 14 | export = colorguard; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/connect-livereload.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'connect-livereload' { 2 | function connectLivereload(options?: any): any; 3 | module connectLivereload {} 4 | export = connectLivereload; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/cssnano.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'cssnano' { 2 | 3 | interface IOptions { 4 | discardComments: { 5 | removeAll: boolean; 6 | }; 7 | } 8 | 9 | interface ICssnano { 10 | (opts?: IOptions): NodeJS.ReadWriteStream; 11 | } 12 | 13 | const cssnano: ICssnano; 14 | export = cssnano; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/doiuse.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'doiuse' { 2 | 3 | interface IOptions { 4 | browsers?: string[]; 5 | ignore?: string[]; 6 | ignoreFiles?: string[]; 7 | onFeatureUsage?: Function; 8 | } 9 | 10 | interface IDoiuse { 11 | (opts?: IOptions): NodeJS.ReadWriteStream; 12 | } 13 | 14 | const doiuse: IDoiuse; 15 | export = doiuse; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/istream.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'isstream' { 2 | function istream(stream: any): boolean; 3 | interface Istream { 4 | isReadable(stream: any): boolean; 5 | isWritable(stream: any): boolean; 6 | isDuplex(stream: any): boolean; 7 | } 8 | module istream {} 9 | export = istream; 10 | } 11 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/karma.d.ts: -------------------------------------------------------------------------------- 1 | // Use this minimalistic definition file as bluebird dependency 2 | // generate a lot of errors. 3 | 4 | declare module 'karma' { 5 | var karma: IKarma; 6 | export = karma; 7 | interface IKarma { 8 | server: { 9 | start(options: any, callback: Function): void 10 | }; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/merge-stream.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'merge-stream' { 2 | function mergeStream(...streams: NodeJS.ReadWriteStream[]): MergeStream; 3 | interface MergeStream extends NodeJS.ReadWriteStream { 4 | add(stream: NodeJS.ReadWriteStream): MergeStream; 5 | } 6 | module mergeStream {} 7 | export = mergeStream; 8 | } -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/open.d.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/borisyankov/DefinitelyTyped/tree/master/open 2 | // Does not support ES2015 import (import * as open from 'open'). 3 | 4 | declare module 'open' { 5 | function open(target: string, app?: string): void; 6 | module open {} 7 | export = open; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/postcss-reporter.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'postcss-reporter' { 2 | 3 | interface IOptions { 4 | clearMessages?: boolean; 5 | formatter?: Function; 6 | plugins?: string[]; 7 | throwError?: boolean; 8 | sortByPosition?: boolean; 9 | positionless?: string; 10 | noIcon?: boolean; 11 | noPlugin?: boolean; 12 | } 13 | 14 | interface IPostcssReporter { 15 | (opts?: IOptions): NodeJS.ReadWriteStream; 16 | } 17 | 18 | const postcssReporter: IPostcssReporter; 19 | export = postcssReporter; 20 | } 21 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/slash.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'slash' { 2 | function slash(path: string): string; 3 | module slash {} 4 | export = slash; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/stylelint.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'stylelint' { 2 | 3 | interface IOptions { 4 | config?: Object; 5 | configFile?: string; 6 | configBasedir?: string; 7 | configOverrides?: Object; 8 | } 9 | 10 | interface IStylelint { 11 | (opts?: IOptions): NodeJS.ReadWriteStream; 12 | } 13 | 14 | const stylelint: IStylelint; 15 | export = stylelint; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/systemjs-builder.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'systemjs-builder' { 2 | class Builder { 3 | constructor(configObject?: any, baseUrl?: string, configPath?: string); 4 | bundle(source: string, target: string, options?: any): Promise; 5 | buildStatic(source: string, target: string, options?: any): Promise; 6 | } 7 | 8 | module Builder {} 9 | export = Builder; 10 | } -------------------------------------------------------------------------------- /frontend/tools/manual_typings/seed/tiny-lr.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'tiny-lr' { 2 | function tinylr(): ITinylr; 3 | module tinylr {} 4 | export = tinylr; 5 | 6 | interface ITinylr { 7 | changed(options: any): void; 8 | listen(port: number): void; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/tools/tasks/project/sample.task.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import {join} from 'path'; 3 | import {APP_SRC, APP_DEST} from '../../config'; 4 | 5 | /** 6 | * Sample tasks 7 | * 8 | */ 9 | 10 | export = () => { 11 | return gulp.src(join(APP_SRC, '**/*.ts')) 12 | .pipe(gulp.dest(APP_DEST)); 13 | } 14 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.assets.dev.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import {join} from 'path'; 3 | import {APP_SRC, APP_DEST} from '../../config'; 4 | 5 | export = () => { 6 | return gulp.src([ 7 | join(APP_SRC, '**'), 8 | '!' + join(APP_SRC, '**', '*.ts') 9 | ]) 10 | .pipe(gulp.dest(APP_DEST)); 11 | } 12 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.assets.prod.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import {join} from 'path'; 3 | import {APP_SRC, APP_DEST, ASSETS_SRC} from '../../config'; 4 | 5 | // TODO There should be more elegant to prevent empty directories from copying 6 | let es: any = require('event-stream'); 7 | var onlyDirs = function (es: any) { 8 | return es.map(function (file: any, cb: any) { 9 | if (file.stat.isFile()) { 10 | return cb(null, file); 11 | } else { 12 | return cb(); 13 | } 14 | }); 15 | }; 16 | 17 | export = () => { 18 | return gulp.src([ 19 | join(APP_SRC, '**'), 20 | '!' + join(APP_SRC, '**', '*.ts'), 21 | '!' + join(APP_SRC, '**', '*.css'), 22 | '!' + join(APP_SRC, '**', '*.html'), 23 | '!' + join(ASSETS_SRC, '**', '*.js') 24 | ]) 25 | .pipe(onlyDirs(es)) 26 | .pipe(gulp.dest(APP_DEST)); 27 | } 28 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.bundles.app.ts: -------------------------------------------------------------------------------- 1 | import {join} from 'path'; 2 | import * as Builder from 'systemjs-builder'; 3 | import { 4 | BOOTSTRAP_MODULE, 5 | JS_PROD_APP_BUNDLE, 6 | JS_DEST, 7 | SYSTEM_BUILDER_CONFIG, 8 | TMP_DIR 9 | } from '../../config'; 10 | 11 | const BUNDLER_OPTIONS = { 12 | format: 'cjs', 13 | minify: true, 14 | mangle: false 15 | }; 16 | 17 | export = (done: any) => { 18 | let builder = new Builder(SYSTEM_BUILDER_CONFIG); 19 | builder 20 | .buildStatic(join(TMP_DIR, BOOTSTRAP_MODULE), 21 | join(JS_DEST, JS_PROD_APP_BUNDLE), 22 | BUNDLER_OPTIONS) 23 | .then(() => done()); 24 | }; 25 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.bundles.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import * as merge from 'merge-stream'; 4 | import {DEPENDENCIES, JS_PROD_SHIMS_BUNDLE, JS_DEST} from '../../config'; 5 | const plugins = gulpLoadPlugins(); 6 | 7 | export = () => merge(bundleShims()); 8 | 9 | function getShims() { 10 | let libs = DEPENDENCIES 11 | .filter(d => /\.js$/.test(d.src)); 12 | 13 | return libs.filter(l => l.inject === 'shims') 14 | .concat(libs.filter(l => l.inject === 'libs')) 15 | .concat(libs.filter(l => l.inject === true)) 16 | .map(l => l.src); 17 | } 18 | 19 | function bundleShims() { 20 | return gulp.src(getShims()) 21 | // Strip comments and sourcemaps 22 | .pipe(plugins.uglify({ 23 | mangle: false 24 | })) 25 | .pipe(plugins.concat(JS_PROD_SHIMS_BUNDLE)) 26 | .pipe(gulp.dest(JS_DEST)); 27 | } 28 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.docs.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import {join} from 'path'; 4 | import {APP_SRC, APP_TITLE, DOCS_DEST} from '../../config'; 5 | const plugins = gulpLoadPlugins(); 6 | 7 | export = () => { 8 | 9 | let src = [ 10 | 'typings/main.d.ts', 11 | join(APP_SRC, '**/*.ts'), 12 | '!' + join(APP_SRC, '**/*.spec.ts'), 13 | '!' + join(APP_SRC, '**/*.e2e.ts') 14 | ]; 15 | 16 | return gulp.src(src) 17 | .pipe(plugins.typedoc({ 18 | // TypeScript options (see typescript docs) 19 | module: 'commonjs', 20 | target: 'es5', 21 | includeDeclarations: true, 22 | // Output options (see typedoc docs) 23 | out: DOCS_DEST, 24 | json: join(DOCS_DEST , 'data/docs.json' ), 25 | name: APP_TITLE, 26 | ignoreCompilerErrors: false, 27 | experimentalDecorators: true, 28 | version: true 29 | })); 30 | } 31 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.html_css.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import * as merge from 'merge-stream'; 4 | import * as autoprefixer from 'autoprefixer'; 5 | import * as cssnano from 'cssnano'; 6 | import {join} from 'path'; 7 | import {APP_SRC, TMP_DIR, CSS_PROD_BUNDLE, CSS_DEST, APP_DEST, BROWSER_LIST, ENV, DEPENDENCIES} from '../../config'; 8 | const plugins = gulpLoadPlugins(); 9 | 10 | const processors = [ 11 | autoprefixer({ 12 | browsers: BROWSER_LIST 13 | }) 14 | ]; 15 | 16 | const isProd = ENV === 'prod'; 17 | 18 | if (isProd) { 19 | processors.push( 20 | cssnano({ 21 | discardComments: {removeAll: true} 22 | }) 23 | ); 24 | } 25 | 26 | function prepareTemplates() { 27 | return gulp.src(join(APP_SRC, '**', '*.html')) 28 | .pipe(gulp.dest(TMP_DIR)); 29 | } 30 | 31 | function processComponentCss() { 32 | return gulp.src([ 33 | join(APP_SRC, '**', '*.css'), 34 | '!' + join(APP_SRC, 'assets', '**', '*.css') 35 | ]) 36 | .pipe(isProd ? plugins.cached('process-component-css') : plugins.util.noop()) 37 | .pipe(plugins.postcss(processors)) 38 | .pipe(gulp.dest(isProd ? TMP_DIR: APP_DEST)); 39 | } 40 | 41 | function processExternalCss() { 42 | return gulp.src(getExternalCss().map(r => r.src)) 43 | .pipe(isProd ? plugins.cached('process-external-css') : plugins.util.noop()) 44 | .pipe(plugins.postcss(processors)) 45 | .pipe(isProd ? plugins.concat(CSS_PROD_BUNDLE) : plugins.util.noop()) 46 | .pipe(gulp.dest(CSS_DEST)); 47 | } 48 | 49 | function getExternalCss() { 50 | return DEPENDENCIES.filter(d => /\.css$/.test(d.src)); 51 | } 52 | 53 | 54 | export = () => merge(processComponentCss(), prepareTemplates(), processExternalCss()); 55 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.index.dev.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import {join} from 'path'; 4 | import * as slash from 'slash'; 5 | import {APP_SRC, APP_DEST, APP_BASE, DEPENDENCIES} from '../../config'; 6 | import {templateLocals} from '../../utils'; 7 | const plugins = gulpLoadPlugins(); 8 | 9 | export = () => { 10 | return gulp.src(join(APP_SRC, 'index.html')) 11 | .pipe(inject('shims')) 12 | .pipe(inject('libs')) 13 | .pipe(inject()) 14 | .pipe(plugins.template(templateLocals())) 15 | .pipe(gulp.dest(APP_DEST)); 16 | }; 17 | 18 | 19 | function inject(name?: string) { 20 | return plugins.inject(gulp.src(getInjectablesDependenciesRef(name), { read: false }), { 21 | name, 22 | transform: transformPath() 23 | }); 24 | } 25 | 26 | function getInjectablesDependenciesRef(name?: string) { 27 | return DEPENDENCIES 28 | .filter(dep => dep['inject'] && dep['inject'] === (name || true)) 29 | .map(mapPath); 30 | } 31 | 32 | function mapPath(dep: any) { 33 | let envPath = dep.src; 34 | if (envPath.startsWith(APP_SRC)) { 35 | envPath = join(APP_DEST, dep.src.replace(APP_SRC, '')); 36 | } 37 | return envPath; 38 | } 39 | 40 | function transformPath() { 41 | return function (filepath: string) { 42 | arguments[0] = join(APP_BASE, filepath) + `?${Date.now()}`; 43 | return slash(plugins.inject.transform.apply(plugins.inject.transform, arguments)); 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.index.prod.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import {join, sep, normalize} from 'path'; 4 | import * as slash from 'slash'; 5 | import {templateLocals} from '../../utils'; 6 | import { 7 | APP_SRC, 8 | APP_DEST, 9 | CSS_DEST, 10 | JS_DEST, 11 | CSS_PROD_BUNDLE, 12 | JS_PROD_APP_BUNDLE, 13 | JS_PROD_SHIMS_BUNDLE 14 | } from '../../config'; 15 | const plugins = gulpLoadPlugins(); 16 | 17 | export = () => { 18 | return gulp.src(join(APP_SRC, 'index.html')) 19 | .pipe(injectJs()) 20 | .pipe(injectCss()) 21 | .pipe(plugins.template(templateLocals())) 22 | .pipe(gulp.dest(APP_DEST)); 23 | } 24 | 25 | function inject(...files: Array) { 26 | return plugins.inject(gulp.src(files, { read: false }), { 27 | files, 28 | transform: transformPath() 29 | }); 30 | } 31 | 32 | function injectJs() { 33 | return inject(join(JS_DEST, JS_PROD_SHIMS_BUNDLE), join(JS_DEST, JS_PROD_APP_BUNDLE)); 34 | } 35 | 36 | function injectCss() { 37 | return inject(join(CSS_DEST, CSS_PROD_BUNDLE)); 38 | } 39 | 40 | function transformPath() { 41 | return function(filepath: string) { 42 | let path: Array = normalize(filepath).split(sep); 43 | arguments[0] = path.slice(3, path.length).join(sep) + `?${Date.now()}`; 44 | return slash(plugins.inject.transform.apply(plugins.inject.transform, arguments)); 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.js.dev.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import {join} from 'path'; 4 | import {APP_SRC, APP_DEST, TOOLS_DIR} from '../../config'; 5 | import {templateLocals, makeTsProject} from '../../utils'; 6 | const plugins = gulpLoadPlugins(); 7 | 8 | export = () => { 9 | let tsProject = makeTsProject(); 10 | let src = [ 11 | 'typings/browser.d.ts', 12 | TOOLS_DIR + '/manual_typings/**/*.d.ts', 13 | join(APP_SRC, '**/*.ts'), 14 | '!' + join(APP_SRC, '**/*.spec.ts'), 15 | '!' + join(APP_SRC, '**/*.e2e.ts') 16 | ]; 17 | let result = gulp.src(src) 18 | .pipe(plugins.plumber()) 19 | .pipe(plugins.sourcemaps.init()) 20 | .pipe(plugins.typescript(tsProject)); 21 | 22 | return result.js 23 | .pipe(plugins.sourcemaps.write()) 24 | .pipe(plugins.template(templateLocals())) 25 | .pipe(gulp.dest(APP_DEST)); 26 | }; 27 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.js.e2e.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import {join} from 'path'; 4 | import {APP_SRC, APP_DEST, TOOLS_DIR} from '../../config'; 5 | import {templateLocals, makeTsProject} from '../../utils'; 6 | const plugins = gulpLoadPlugins(); 7 | 8 | 9 | export = () => { 10 | let tsProject = makeTsProject(); 11 | let src = [ 12 | 'typings/browser.d.ts', 13 | TOOLS_DIR + '/manual_typings/**/*.d.ts', 14 | join(APP_SRC, '**/*.ts'), 15 | '!' + join(APP_SRC, '**/*.spec.ts') 16 | ]; 17 | let result = gulp.src(src) 18 | .pipe(plugins.plumber()) 19 | .pipe(plugins.sourcemaps.init()) 20 | .pipe(plugins.typescript(tsProject)); 21 | 22 | return result.js 23 | .pipe(plugins.sourcemaps.write()) 24 | .pipe(plugins.template(templateLocals())) 25 | .pipe(gulp.dest(APP_DEST)); 26 | } 27 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.js.prod.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import {join} from 'path'; 4 | import {TMP_DIR, TOOLS_DIR} from '../../config'; 5 | import {templateLocals, makeTsProject} from '../../utils'; 6 | const plugins = gulpLoadPlugins(); 7 | 8 | const INLINE_OPTIONS = { 9 | base: TMP_DIR, 10 | useRelativePaths: true, 11 | removeLineBreaks: true 12 | }; 13 | 14 | export = () => { 15 | let tsProject = makeTsProject(); 16 | let src = [ 17 | 'typings/browser.d.ts', 18 | TOOLS_DIR + '/manual_typings/**/*.d.ts', 19 | join(TMP_DIR, '**/*.ts') 20 | ]; 21 | let result = gulp.src(src) 22 | .pipe(plugins.plumber()) 23 | .pipe(plugins.inlineNg2Template(INLINE_OPTIONS)) 24 | .pipe(plugins.typescript(tsProject)); 25 | 26 | return result.js 27 | .pipe(plugins.template(templateLocals())) 28 | .pipe(gulp.dest(TMP_DIR)); 29 | } 30 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.js.test.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import {join} from 'path'; 4 | import {BOOTSTRAP_MODULE, APP_SRC, APP_DEST, TOOLS_DIR} from '../../config'; 5 | import {makeTsProject} from '../../utils'; 6 | const plugins = gulpLoadPlugins(); 7 | 8 | export = () => { 9 | let tsProject = makeTsProject(); 10 | let src = [ 11 | 'typings/browser.d.ts', 12 | TOOLS_DIR + '/manual_typings/**/*.d.ts', 13 | join(APP_SRC, '**/*.ts'), 14 | '!' + join(APP_SRC, '**/*.e2e.ts'), 15 | '!' + join(APP_SRC, `${BOOTSTRAP_MODULE}.ts`) 16 | ]; 17 | let result = gulp.src(src) 18 | .pipe(plugins.plumber()) 19 | .pipe(plugins.sourcemaps.init()) 20 | .pipe(plugins.inlineNg2Template({base: APP_SRC, useRelativePaths: true})) 21 | .pipe(plugins.typescript(tsProject)); 22 | 23 | return result.js 24 | .pipe(plugins.sourcemaps.write()) 25 | .pipe(gulp.dest(APP_DEST)); 26 | } 27 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/build.js.tools.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import {join} from 'path'; 4 | import {TOOLS_DIR} from '../../config'; 5 | import {templateLocals, makeTsProject} from '../../utils'; 6 | const plugins = gulpLoadPlugins(); 7 | 8 | export = () => { 9 | let tsProject = makeTsProject(); 10 | let src = [ 11 | 'typings/main.d.ts', 12 | TOOLS_DIR + '/manual_typings/**/*.d.ts', 13 | join(TOOLS_DIR, '**/*.ts') 14 | ]; 15 | let result = gulp.src(src, {base: './'}) 16 | .pipe(plugins.plumber()) 17 | .pipe(plugins.sourcemaps.init()) 18 | .pipe(plugins.typescript(tsProject)); 19 | 20 | return result.js 21 | .pipe(plugins.sourcemaps.write()) 22 | .pipe(plugins.template(templateLocals())) 23 | .pipe(gulp.dest('./')); 24 | } 25 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/check.versions.ts: -------------------------------------------------------------------------------- 1 | import {VERSION_NPM, VERSION_NODE} from '../../config'; 2 | 3 | function reportError(message: string) { 4 | console.error(require('chalk').white.bgRed.bold(message)); 5 | process.exit(1); 6 | } 7 | 8 | export = () => { 9 | let exec = require('child_process').exec; 10 | let semver = require('semver'); 11 | 12 | exec('npm --version', 13 | function (error: Error, stdout: NodeBuffer, stderr: NodeBuffer) { 14 | if (error !== null) { 15 | reportError('npm preinstall error: ' + error + stderr); 16 | } 17 | 18 | if (!semver.gte(stdout, VERSION_NPM)) { 19 | reportError('NPM is not in required version! Required is ' + VERSION_NPM + ' and you\'re using ' + stdout); 20 | } 21 | }); 22 | 23 | exec('node --version', 24 | function (error: Error, stdout: NodeBuffer, stderr: NodeBuffer) { 25 | if (error !== null) { 26 | reportError('npm preinstall error: ' + error + stderr); 27 | } 28 | 29 | if (!semver.gte(stdout, VERSION_NODE)) { 30 | reportError('NODE is not in required version! Required is ' + VERSION_NODE + ' and you\'re using ' + stdout); 31 | } 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/clean.all.ts: -------------------------------------------------------------------------------- 1 | import {DIST_DIR} from '../../config'; 2 | import {clean} from '../../utils'; 3 | 4 | export = clean(DIST_DIR) 5 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/clean.dev.ts: -------------------------------------------------------------------------------- 1 | import {DEV_DEST} from '../../config'; 2 | import {clean} from '../../utils'; 3 | 4 | export = clean(DEV_DEST) 5 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/clean.prod.ts: -------------------------------------------------------------------------------- 1 | import {PROD_DEST, TMP_DIR} from '../../config'; 2 | import {clean} from '../../utils'; 3 | 4 | export = clean([PROD_DEST, TMP_DIR]) 5 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/clean.tools.ts: -------------------------------------------------------------------------------- 1 | import * as util from 'gulp-util'; 2 | import * as chalk from 'chalk'; 3 | import * as rimraf from 'rimraf'; 4 | import {readdirSync, lstatSync} from 'fs'; 5 | import {join} from 'path'; 6 | import {TOOLS_DIR} from '../../config'; 7 | 8 | export = (done: any) => { 9 | deleteAndWalk(TOOLS_DIR); 10 | done(); 11 | } 12 | 13 | function walk(path: any) { 14 | let files = readdirSync(path); 15 | for (let i = 0; i < files.length; i += 1) { 16 | let curPath = join(path, files[i]); 17 | if (lstatSync(curPath).isDirectory()) { // recurse 18 | deleteAndWalk(curPath); 19 | } 20 | } 21 | } 22 | 23 | function deleteAndWalk(path: any) { 24 | try { 25 | rimraf.sync(join(path, '*.js')); 26 | util.log('Deleted', chalk.yellow(`${path}/*.js`)); 27 | } catch (e) { 28 | util.log('Error while deleting', chalk.yellow(`${path}/*.js`), e); 29 | } 30 | walk(path); 31 | } 32 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/copy.js.prod.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import {join} from 'path'; 3 | import {APP_SRC, TMP_DIR} from '../../config'; 4 | 5 | export = () => { 6 | return gulp.src([ 7 | join(APP_SRC, '**/*.ts'), 8 | '!' + join(APP_SRC, '**/*.spec.ts'), 9 | '!' + join(APP_SRC, '**/*.e2e.ts') 10 | ]) 11 | .pipe(gulp.dest(TMP_DIR)); 12 | }; 13 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/css-lint.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import * as merge from 'merge-stream'; 4 | import * as reporter from 'postcss-reporter'; 5 | import * as stylelint from 'stylelint'; 6 | import * as doiuse from 'doiuse'; 7 | import * as colorguard from 'colorguard'; 8 | import {join} from 'path'; 9 | import {APP_SRC, APP_ASSETS, BROWSER_LIST, ENV} from '../../config'; 10 | const plugins = gulpLoadPlugins(); 11 | 12 | const isProd = ENV === 'prod'; 13 | 14 | const processors = [ 15 | doiuse({ 16 | browsers: BROWSER_LIST, 17 | }), 18 | colorguard(), 19 | stylelint(), 20 | reporter({clearMessages: true}) 21 | ]; 22 | 23 | function lintComponentCss() { 24 | return gulp.src([ 25 | join(APP_SRC, '**', '*.css'), 26 | '!' + join(APP_SRC, 'assets', '**', '*.css') 27 | ]) 28 | .pipe(isProd ? plugins.cached('css-lint') : plugins.util.noop()) 29 | .pipe(plugins.postcss(processors)); 30 | } 31 | 32 | function lintExternalCss() { 33 | return gulp.src(getExternalCss().map(r => r.src)) 34 | .pipe(isProd ? plugins.cached('css-lint') : plugins.util.noop()) 35 | .pipe(plugins.postcss(processors)); 36 | } 37 | 38 | function getExternalCss() { 39 | return APP_ASSETS.filter(d => /\.css$/.test(d.src) && !d.vendor); 40 | } 41 | 42 | 43 | export = () => merge(lintComponentCss(), lintExternalCss()); 44 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/karma.start.ts: -------------------------------------------------------------------------------- 1 | import * as karma from 'karma'; 2 | import {join} from 'path'; 3 | 4 | export = (done: any) => { 5 | new (karma).Server({ 6 | configFile: join(process.cwd(), 'karma.conf.js'), 7 | singleRun: true 8 | }).start(done); 9 | } 10 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/serve.coverage.ts: -------------------------------------------------------------------------------- 1 | import {serveCoverage} from '../../utils'; 2 | 3 | export = serveCoverage 4 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/serve.docs.ts: -------------------------------------------------------------------------------- 1 | import {serveDocs} from '../../utils'; 2 | 3 | export = serveDocs 4 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/server.start.ts: -------------------------------------------------------------------------------- 1 | import {serveSPA} from '../../utils'; 2 | 3 | export = serveSPA 4 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/tslint.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 3 | import {join} from 'path'; 4 | import {APP_SRC, TOOLS_DIR, NG2LINT_RULES} from '../../config'; 5 | const plugins = gulpLoadPlugins(); 6 | 7 | export = () => { 8 | let src = [ 9 | join(APP_SRC, '**/*.ts'), 10 | '!' + join(APP_SRC, '**/*.d.ts'), 11 | join(TOOLS_DIR, '**/*.ts'), 12 | '!' + join(TOOLS_DIR, '**/*.d.ts') 13 | ]; 14 | 15 | return gulp.src(src) 16 | .pipe(plugins.tslint({ 17 | rulesDirectory: NG2LINT_RULES 18 | })) 19 | .pipe(plugins.tslint.report(plugins.tslintStylish, { 20 | emitError: true, 21 | sort: true, 22 | bell: true 23 | })); 24 | }; 25 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/watch.dev.ts: -------------------------------------------------------------------------------- 1 | import {watch} from '../../utils'; 2 | 3 | export = watch('build.dev') 4 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/watch.e2e.ts: -------------------------------------------------------------------------------- 1 | import {watch} from '../../utils'; 2 | 3 | export = watch('build.e2e') 4 | -------------------------------------------------------------------------------- /frontend/tools/tasks/seed/watch.test.ts: -------------------------------------------------------------------------------- 1 | import {watch} from '../../utils'; 2 | 3 | export = watch('build.test') 4 | -------------------------------------------------------------------------------- /frontend/tools/utils.ts: -------------------------------------------------------------------------------- 1 | export * from './utils/seed.utils'; 2 | export * from './utils/project.utils'; 3 | -------------------------------------------------------------------------------- /frontend/tools/utils/project.utils.ts: -------------------------------------------------------------------------------- 1 | export * from './project/sample_util'; 2 | -------------------------------------------------------------------------------- /frontend/tools/utils/project/sample_util.ts: -------------------------------------------------------------------------------- 1 | export function myUtil() { 2 | // ... 3 | } 4 | -------------------------------------------------------------------------------- /frontend/tools/utils/seed.utils.ts: -------------------------------------------------------------------------------- 1 | export * from './seed/clean'; 2 | export * from './seed/code_change_tools'; 3 | export * from './seed/server'; 4 | export * from './seed/tasks_tools'; 5 | export * from './seed/template_locals'; 6 | export * from './seed/tsproject'; 7 | export * from './seed/watch'; 8 | -------------------------------------------------------------------------------- /frontend/tools/utils/seed/clean.ts: -------------------------------------------------------------------------------- 1 | import * as util from 'gulp-util'; 2 | import * as chalk from 'chalk'; 3 | import * as rimraf from 'rimraf'; 4 | 5 | export function clean(paths: string|string[]): (done: () => void) => void { 6 | return done => { 7 | let pathsArray: string[]; 8 | if (!(paths instanceof Array)) { 9 | pathsArray = [paths]; 10 | } else pathsArray = paths; 11 | let promises = pathsArray.map(p => { 12 | return new Promise(resolve => { 13 | rimraf(p, e => { 14 | if (e) { 15 | util.log('Clean task failed with', e); 16 | } else { 17 | util.log('Deleted', chalk.yellow(p || '-')); 18 | } 19 | resolve(); 20 | }); 21 | }); 22 | }); 23 | Promise.all(promises).then(() => done()); 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /frontend/tools/utils/seed/code_change_tools.ts: -------------------------------------------------------------------------------- 1 | import {PORT, APP_DEST, APP_BASE, DIST_DIR} from '../../config'; 2 | import * as browserSync from 'browser-sync'; 3 | const proxy = require('proxy-middleware'); 4 | 5 | let runServer = () => { 6 | let baseDir = APP_DEST; 7 | let routes:any = { 8 | [`${APP_BASE}${APP_DEST}`]: APP_DEST, 9 | [`${APP_BASE}node_modules`]: 'node_modules', 10 | }; 11 | 12 | if (APP_BASE !== '/') { 13 | routes[`${APP_BASE}`] = APP_DEST; 14 | baseDir = `${DIST_DIR}/empty/`; 15 | } 16 | 17 | browserSync({ 18 | port: PORT, 19 | startPath: APP_BASE, 20 | server: { 21 | baseDir: baseDir, 22 | middleware: [ 23 | proxy({ 24 | protocol: 'http:', 25 | hostname: 'localhost', 26 | port: 3000, 27 | pathname: '/api', 28 | route: '/api' 29 | }), 30 | require('connect-history-api-fallback')({index: `${APP_BASE}index.html`}) 31 | ], 32 | routes: routes 33 | } 34 | }); 35 | }; 36 | 37 | let listen = () => { 38 | // if (ENABLE_HOT_LOADING) { 39 | // ng2HotLoader.listen({ 40 | // port: HOT_LOADER_PORT, 41 | // processPath: file => { 42 | // return file.replace(join(PROJECT_ROOT, APP_SRC), join('dist', 'dev')); 43 | // } 44 | // }); 45 | // } 46 | runServer(); 47 | }; 48 | 49 | let changed = (files: any) => { 50 | if (!(files instanceof Array)) { 51 | files = [files]; 52 | } 53 | // if (ENABLE_HOT_LOADING) { 54 | // ng2HotLoader.onChange(files); 55 | // } else { 56 | //TODO: Figure out why you can't pass a file to reload 57 | browserSync.reload(files.path); 58 | //} 59 | }; 60 | 61 | export { listen, changed }; 62 | -------------------------------------------------------------------------------- /frontend/tools/utils/seed/server.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | import * as openResource from 'open'; 3 | import * as serveStatic from 'serve-static'; 4 | import * as codeChangeTool from './code_change_tools'; 5 | import {resolve} from 'path'; 6 | import {APP_BASE, DOCS_DEST, DOCS_PORT, COVERAGE_PORT} from '../../config'; 7 | 8 | export function serveSPA() { 9 | codeChangeTool.listen(); 10 | } 11 | 12 | export function notifyLiveReload(e:any) { 13 | let fileName = e.path; 14 | codeChangeTool.changed(fileName); 15 | } 16 | 17 | export function serveDocs() { 18 | let server = express(); 19 | 20 | server.use( 21 | APP_BASE, 22 | serveStatic(resolve(process.cwd(), DOCS_DEST)) 23 | ); 24 | 25 | server.listen(DOCS_PORT, () => 26 | openResource('http://localhost:' + DOCS_PORT + APP_BASE) 27 | ); 28 | } 29 | 30 | export function serveCoverage() { 31 | let server = express(); 32 | 33 | server.use( 34 | APP_BASE, 35 | serveStatic(resolve(process.cwd(), 'coverage')) 36 | ); 37 | 38 | server.listen(COVERAGE_PORT, () => 39 | openResource('http://localhost:' + COVERAGE_PORT + APP_BASE) 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /frontend/tools/utils/seed/tasks_tools.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as util from 'gulp-util'; 3 | import * as chalk from 'chalk'; 4 | import * as isstream from 'isstream'; 5 | import {readdirSync, existsSync, lstatSync} from 'fs'; 6 | import {join} from 'path'; 7 | 8 | 9 | export function loadTasks(path: string): void { 10 | util.log('Loading tasks folder', chalk.yellow(path)); 11 | readDir(path, taskname => registerTask(taskname, path)); 12 | } 13 | 14 | function registerTask(taskname: string, path: string): void { 15 | const TASK = join(path, taskname); 16 | util.log('Registering task', chalk.yellow(TASK)); 17 | 18 | gulp.task(taskname, (done: any) => { 19 | const task = require(TASK); 20 | if (task.length > 0) { 21 | return task(done); 22 | } 23 | 24 | const taskReturnedValue = task(); 25 | if (isstream(taskReturnedValue)) { 26 | return taskReturnedValue; 27 | } 28 | 29 | // TODO: add promise handling if needed at some point. 30 | 31 | done(); 32 | }); 33 | } 34 | 35 | function readDir(root: string, cb: (taskname: string) => void) { 36 | if (!existsSync(root)) return; 37 | 38 | walk(root); 39 | 40 | function walk(path: string) { 41 | let files = readdirSync(path); 42 | for (let i = 0; i < files.length; i += 1) { 43 | let file = files[i]; 44 | let curPath = join(path, file); 45 | if (lstatSync(curPath).isFile() && /\.ts$/.test(file)) { 46 | let taskname = file.replace(/(\.ts)/, ''); 47 | cb(taskname); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /frontend/tools/utils/seed/template_locals.ts: -------------------------------------------------------------------------------- 1 | import * as CONFIG from '../../config'; 2 | 3 | // TODO: Add an interface to register more template locals. 4 | export function templateLocals() { 5 | return CONFIG; 6 | } 7 | -------------------------------------------------------------------------------- /frontend/tools/utils/seed/tsproject.ts: -------------------------------------------------------------------------------- 1 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 2 | const plugins = gulpLoadPlugins(); 3 | 4 | let _tsProject: any; 5 | 6 | export function makeTsProject() { 7 | if (!_tsProject) { 8 | _tsProject = plugins.typescript.createProject('tsconfig.json', { 9 | typescript: require('typescript') 10 | }); 11 | } 12 | return _tsProject; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/tools/utils/seed/watch.ts: -------------------------------------------------------------------------------- 1 | import * as runSequence from 'run-sequence'; 2 | import {notifyLiveReload} from '../../utils'; 3 | import * as gulpLoadPlugins from 'gulp-load-plugins'; 4 | import {join} from 'path'; 5 | import {APP_SRC} from '../../config'; 6 | const plugins = gulpLoadPlugins(); 7 | 8 | export function watch(taskname: string) { 9 | return function () { 10 | plugins.watch(join(APP_SRC, '**'), (e:any) => 11 | runSequence(taskname, () => notifyLiveReload(e)) 12 | ); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "declaration": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "sourceMap": true, 11 | "pretty": true, 12 | "allowUnreachableCode": false, 13 | "allowUnusedLabels": false, 14 | "noImplicitAny": false, 15 | "noImplicitReturns": true, 16 | "noImplicitUseStrict": false, 17 | "noFallthroughCasesInSwitch": true 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | "typings/browser.d.ts", 22 | "typings/browser" 23 | ], 24 | "compileOnSave": false 25 | } 26 | -------------------------------------------------------------------------------- /frontend/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["node_modules/ng2lint/dist/src"], 3 | "rules": { 4 | "class-name": true, 5 | "curly": false, 6 | "eofline": true, 7 | "indent": ["spaces"], 8 | "max-line-length": [true, 140], 9 | "member-ordering": [true, 10 | "public-before-private", 11 | "static-before-instance", 12 | "variables-before-functions" 13 | ], 14 | "no-arg": true, 15 | "no-construct": true, 16 | "no-duplicate-key": true, 17 | "no-duplicate-variable": true, 18 | "no-empty": true, 19 | "no-eval": true, 20 | "no-trailing-whitespace": true, 21 | "no-unused-expression": true, 22 | "no-unused-variable": true, 23 | "no-unreachable": true, 24 | "no-use-before-declare": true, 25 | "one-line": [true, 26 | "check-open-brace", 27 | "check-catch", 28 | "check-else", 29 | "check-whitespace" 30 | ], 31 | "quotemark": [true, "single"], 32 | "semicolon": true, 33 | "trailing-comma": true, 34 | "triple-equals": true, 35 | "variable-name": false, 36 | 37 | "component-selector-name": [true, "kebab-case"], 38 | "component-selector-type": [true, "element"], 39 | "host-parameter-decorator": true, 40 | "input-parameter-decorator": true, 41 | "output-parameter-decorator": true, 42 | "attribute-parameter-decorator": true, 43 | "input-property-directive": true, 44 | "output-property-directive": true 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontend/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "xhr2": "github:gdi2290/typed-xhr2#69f2b8d40d0cd407c1b2a2f2f41fecc6852cabbb" 4 | }, 5 | "devDependencies": {}, 6 | "ambientDependencies": { 7 | "Q": "github:DefinitelyTyped/DefinitelyTyped/q/Q.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 8 | "angular-protractor": "github:DefinitelyTyped/DefinitelyTyped/angular-protractor/angular-protractor.d.ts#64b25f63f0ec821040a5d3e049a976865062ed9d", 9 | "async": "github:DefinitelyTyped/DefinitelyTyped/async/async.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 10 | "browser-sync": "github:DefinitelyTyped/DefinitelyTyped/browser-sync/browser-sync.d.ts#d9b5b35ba3a79ac778c5890531393442439df5dd", 11 | "chalk": "github:DefinitelyTyped/DefinitelyTyped/chalk/chalk.d.ts#5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff", 12 | "chokidar": "github:DefinitelyTyped/DefinitelyTyped/chokidar/chokidar.d.ts#ae81e340a9e897167cfef3122a5a3aed04293814", 13 | "es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#6697d6f7dadbf5773cb40ecda35a76027e0783b2", 14 | "express": "github:DefinitelyTyped/DefinitelyTyped/express/express.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 15 | "glob": "github:DefinitelyTyped/DefinitelyTyped/glob/glob.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 16 | "gulp": "github:DefinitelyTyped/DefinitelyTyped/gulp/gulp.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 17 | "gulp-load-plugins": "github:DefinitelyTyped/DefinitelyTyped/gulp-load-plugins/gulp-load-plugins.d.ts#e081148d88b857d66509e3b46edbd08b3f75f96a", 18 | "gulp-shell": "github:DefinitelyTyped/DefinitelyTyped/gulp-shell/gulp-shell.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 19 | "gulp-util": "github:DefinitelyTyped/DefinitelyTyped/gulp-util/gulp-util.d.ts#5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff", 20 | "jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#26c98c8a9530c44f8c801ccc3b2057e2101187ee", 21 | "micromatch": "github:DefinitelyTyped/DefinitelyTyped/micromatch/micromatch.d.ts#7cda84786520fd0673c934fde1aa722083e05f7b", 22 | "mime": "github:DefinitelyTyped/DefinitelyTyped/mime/mime.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 23 | "minimatch": "github:DefinitelyTyped/DefinitelyTyped/minimatch/minimatch.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 24 | "ng2": "github:gdi2290/typings-ng2/ng2.d.ts#32998ff5584c0eab0cd9dc7704abb1c5c450701c", 25 | "node": "github:DefinitelyTyped/DefinitelyTyped/node/node.d.ts#138ad74b9e8e6c08af7633964962835add4c91e2", 26 | "orchestrator": "github:DefinitelyTyped/DefinitelyTyped/orchestrator/orchestrator.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 27 | "parse-glob": "github:DefinitelyTyped/DefinitelyTyped/parse-glob/parse-glob.d.ts#1ae2ddf27c6e9b98ad7e7fa496788f1aadf37933", 28 | "rimraf": "github:DefinitelyTyped/DefinitelyTyped/rimraf/rimraf.d.ts#09f3d7a8dc79f448b538862c3ad5872f75112d60", 29 | "run-sequence": "github:DefinitelyTyped/DefinitelyTyped/run-sequence/run-sequence.d.ts#052725d74978d6b8d7c4ff537b5a3b21ee755a49", 30 | "selenium-webdriver": "github:DefinitelyTyped/DefinitelyTyped/selenium-webdriver/selenium-webdriver.d.ts#a83677ed13add14c2ab06c7325d182d0ba2784ea", 31 | "serve-static": "github:DefinitelyTyped/DefinitelyTyped/serve-static/serve-static.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 32 | "systemjs": "github:DefinitelyTyped/DefinitelyTyped/systemjs/systemjs.d.ts#5c3e47967affa3c4128a3875d1664ba206ae1b0f", 33 | "through2": "github:DefinitelyTyped/DefinitelyTyped/through2/through2.d.ts#5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff", 34 | "vinyl": "github:DefinitelyTyped/DefinitelyTyped/vinyl/vinyl.d.ts#5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff", 35 | "vinyl-source-stream": "github:DefinitelyTyped/DefinitelyTyped/vinyl-source-stream/vinyl-source-stream.d.ts#ffceea9dd124d277c4597c7bd12930666ec074c5", 36 | "yargs": "github:DefinitelyTyped/DefinitelyTyped/yargs/yargs.d.ts#6a287502dab374e7d4cbf18ea1ac5dff7f74726a", 37 | "zone.js": "github:DefinitelyTyped/DefinitelyTyped/zone.js/zone.js.d.ts#b923a5aaf013ac84c566f27ba6b5843211981c7a" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /frontend/www/assets/main.css: -------------------------------------------------------------------------------- 1 | /* Reset */ 2 | html, 3 | body, 4 | div { 5 | border: 0; 6 | margin: 0; 7 | padding: 0; 8 | } 9 | 10 | /* Box-sizing border-box */ 11 | * { 12 | box-sizing: border-box; 13 | } 14 | 15 | /* Set up a default font and some padding to provide breathing room */ 16 | body { 17 | font-family: Roboto, "Helvetica Neue", sans-serif; 18 | font-size: 16px; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | 23 | p { 24 | font-weight: 400; 25 | letter-spacing: 0.01em; 26 | line-height: 20px; 27 | margin-bottom: 1em; 28 | margin-top: 1em; 29 | } 30 | 31 | ul { 32 | margin: 10px 0 0 0; 33 | padding: 0 0 0 20px; 34 | } 35 | 36 | li { 37 | font-weight: 400; 38 | margin-top: 4px; 39 | } 40 | 41 | input { 42 | border: 1px solid #106cc8; 43 | font-size: 14px; 44 | height: 40px; 45 | outline: none; 46 | padding: 8px; 47 | } 48 | 49 | button { 50 | background-color: #106cc8; 51 | border-style: none; 52 | color: rgba(255, 255, 255, 0.87); 53 | cursor: pointer; 54 | display: inline-block; 55 | font-size: 14px; 56 | height: 40px; 57 | padding: 8px 18px; 58 | text-decoration: none; 59 | } 60 | 61 | button:hover { 62 | background-color: #28739e; 63 | } 64 | 65 | .form-signin { margin-top: 90px; } 66 | 67 | .top-buffer { margin-top: 20px; } 68 | 69 | .block { width: 100%; } 70 | -------------------------------------------------------------------------------- /frontend/www/assets/svg/more.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/www/components/app.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'angular2/core'; 2 | import {ROUTER_DIRECTIVES, RouteConfig} from 'angular2/router'; 3 | import {TasksComponent} from './tasks'; 4 | import {LoginComponent} from './login'; 5 | import {RegistrationComponent} from './registration'; 6 | 7 | @Component({ 8 | selector: 'app', 9 | moduleId: module.id, 10 | templateUrl: '../templates/app.html', 11 | directives: [ROUTER_DIRECTIVES] 12 | }) 13 | 14 | @RouteConfig([ 15 | { path: '/tasks', name: 'Tasks', component: TasksComponent, useAsDefault: true }, 16 | { path: '/login', name: 'Login', component: LoginComponent }, 17 | { path: '/registration', name: 'Registration', component: RegistrationComponent } 18 | ]) 19 | 20 | export class AppComponent { 21 | } 22 | -------------------------------------------------------------------------------- /frontend/www/components/login.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'angular2/core'; 2 | import {ROUTER_DIRECTIVES} from 'angular2/router'; 3 | import {AuthService} from '../services/auth'; 4 | 5 | @Component({ 6 | moduleId: module.id, 7 | templateUrl: '../templates/login.html', 8 | directives: [ROUTER_DIRECTIVES] 9 | }) 10 | 11 | export class LoginComponent { 12 | user: {}; 13 | 14 | constructor(private _authService: AuthService) { 15 | this.user = {}; 16 | } 17 | 18 | login() { 19 | this._authService.login(this.user); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /frontend/www/components/navbar.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'angular2/core'; 2 | import {ROUTER_DIRECTIVES} from 'angular2/router'; 3 | import {AuthService} from '../services/auth'; 4 | 5 | @Component({ 6 | selector: 'navbar', 7 | moduleId: module.id, 8 | templateUrl: '../templates/navbar.html', 9 | directives: [ROUTER_DIRECTIVES] 10 | }) 11 | 12 | export class NavbarComponent { 13 | constructor(private _authService: AuthService) { 14 | } 15 | 16 | logout() { 17 | this._authService.logout(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /frontend/www/components/registration.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'angular2/core'; 2 | import {AuthService} from '../services/auth'; 3 | import {ROUTER_DIRECTIVES} from 'angular2/router'; 4 | import {FORM_BINDINGS,FormBuilder,ControlGroup,Validators} from 'angular2/common'; 5 | 6 | @Component({ 7 | moduleId: module.id, 8 | templateUrl: '../templates/registration.html', 9 | directives: [ROUTER_DIRECTIVES], 10 | viewBindings: [FORM_BINDINGS] 11 | }) 12 | 13 | export class RegistrationComponent { 14 | registerForm: ControlGroup; 15 | 16 | constructor(private _authService: AuthService, fb: FormBuilder) { 17 | this.registerForm = fb.group({ 18 | email: ['', Validators.required], 19 | password: ['', Validators.compose([Validators.required, Validators.minLength(8)])], 20 | password_confirmation: ['', Validators.compose([Validators.required, Validators.minLength(8)])] 21 | }, {validator: this.matchingPasswords('password', 'password_confirmation')}); 22 | } 23 | 24 | registration() { 25 | this._authService.registration(this.registerForm.value); 26 | } 27 | 28 | matchingPasswords(passwordKey: string, passwordConfirmationKey: string) { 29 | return (group: ControlGroup) => { 30 | let passwordInput = group.controls[passwordKey]; 31 | let passwordConfirmationInput = group.controls[passwordConfirmationKey]; 32 | if (passwordInput.value !== passwordConfirmationInput.value) { 33 | return passwordConfirmationInput.setErrors({notEquivalent: true}); 34 | } 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /frontend/www/components/tasks.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'angular2/core'; 2 | import {CanActivate} from 'angular2/router'; 3 | import {TaskListComponent} from './tasks/list'; 4 | import {TaskFormComponent} from './tasks/form'; 5 | import {NavbarComponent} from './navbar'; 6 | 7 | @Component({ 8 | moduleId: module.id, 9 | templateUrl: '../templates/tasks.html', 10 | directives: [TaskListComponent, TaskFormComponent, NavbarComponent] 11 | }) 12 | 13 | @CanActivate(()=> { 14 | if(!localStorage.getItem('token')) { 15 | window.location.pathname = '/login'; 16 | return false; 17 | } 18 | return true; 19 | }) 20 | 21 | export class TasksComponent { 22 | } 23 | -------------------------------------------------------------------------------- /frontend/www/components/tasks/form.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'angular2/core'; 2 | import {Task} from '../../models/task'; 3 | import {TaskService} from '../../services/task'; 4 | 5 | @Component({ 6 | selector: 'task-form', 7 | moduleId: module.id, 8 | templateUrl: '../../templates/tasks/form.html', 9 | }) 10 | 11 | export class TaskFormComponent { 12 | task: Task; 13 | 14 | constructor(private _taskService: TaskService) { 15 | this.task = new Task('', ''); 16 | } 17 | 18 | submit() { 19 | this._taskService.create(this.task); 20 | this.task = new Task('', ''); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frontend/www/components/tasks/list.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'angular2/core'; 2 | import {Observable} from 'rxjs/Observable'; 3 | import {Task} from '../../models/task'; 4 | import {TaskService} from '../../services/task'; 5 | import {ByFieldPipe} from '../../pipes/by_field'; 6 | 7 | @Component({ 8 | selector: 'task-list', 9 | pipes: [ByFieldPipe], 10 | moduleId: module.id, 11 | templateUrl: '../../templates/tasks/list.html', 12 | }) 13 | 14 | export class TaskListComponent { 15 | tasks: Observable; 16 | editForm: {}; 17 | 18 | constructor(private _taskService: TaskService) { 19 | this.editForm = {}; 20 | this.tasks = this._taskService.tasks$; 21 | this._taskService.load(); 22 | } 23 | 24 | delete(task) { 25 | if (confirm('Are you sure?')) { 26 | this._taskService.delete(task.id); 27 | }; 28 | return false; 29 | } 30 | 31 | update (task) { 32 | this._taskService.update(task); 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= APP_TITLE %> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Loading... 17 | 18 | 35 | 36 | 40 | 41 | 42 | 43 | 44 | <% if (ENV === 'dev') { %> 45 | 50 | <% } %> 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | <% if (ENV === 'dev') { %> 59 | 66 | <% } %> 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /frontend/www/main.ts: -------------------------------------------------------------------------------- 1 | import {provide, enableProdMode} from 'angular2/core'; 2 | import {bootstrap} from 'angular2/platform/browser'; 3 | import {ROUTER_PROVIDERS, APP_BASE_HREF} from 'angular2/router'; 4 | import {HTTP_PROVIDERS, HTTP_BINDINGS, Http} from 'angular2/http'; 5 | import {AuthHttp, AuthConfig} from 'angular2-jwt/angular2-jwt'; 6 | import 'rxjs/add/operator/map'; 7 | import {AppComponent} from './components/app'; 8 | import {TaskService} from './services/task'; 9 | import {AuthService} from './services/auth'; 10 | 11 | if ('<%= ENV %>' === 'prod') { enableProdMode(); } 12 | 13 | bootstrap(AppComponent, [ 14 | ROUTER_PROVIDERS, 15 | HTTP_PROVIDERS, 16 | HTTP_BINDINGS, 17 | TaskService, 18 | AuthService, 19 | provide(APP_BASE_HREF, { useValue: '<%= APP_BASE %>' }), 20 | provide(AuthHttp, { 21 | useFactory: (http) => { 22 | return new AuthHttp(new AuthConfig({ 23 | headerName: 'Authorization', 24 | headerPrefix: 'Bearer', 25 | tokenName: 'token', 26 | tokenGetter: (() => localStorage.getItem('token')), 27 | noJwtError: false 28 | }), http); 29 | }, 30 | deps: [Http] 31 | }) 32 | ]); 33 | -------------------------------------------------------------------------------- /frontend/www/models/task.ts: -------------------------------------------------------------------------------- 1 | export class Task { 2 | id: number; 3 | name: string; 4 | description: string; 5 | done: boolean; 6 | 7 | constructor(name: string, description: string, done?: boolean, id?: number) { 8 | this.id = id || null; 9 | this.name = name; 10 | this.description = description; 11 | this.done = done || false; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/www/pipes/by_field.ts: -------------------------------------------------------------------------------- 1 | import {Pipe} from 'angular2/core'; 2 | 3 | @Pipe({ 4 | name: 'byField', 5 | pure: false 6 | }) 7 | 8 | export class ByFieldPipe { 9 | transform(items, arg) { 10 | if(items) { 11 | return items.filter((item)=> item[arg[0]] === arg[1]); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/www/services/auth.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from 'angular2/core'; 2 | import {Http, Headers} from 'angular2/http'; 3 | import {Router} from 'angular2/router'; 4 | 5 | @Injectable() 6 | export class AuthService { 7 | private headers; 8 | 9 | constructor(public router: Router, public http: Http) { 10 | this.headers = new Headers({'Content-Type': 'application/json'}); 11 | } 12 | 13 | login(user) { 14 | let body = JSON.stringify(user); 15 | this.http.post('/api/sessions', body, { headers: this.headers }).subscribe( 16 | response => { 17 | localStorage.setItem('token', response.json().token); 18 | this.router.navigate(['Tasks']); 19 | }, 20 | error => { 21 | alert(error.text()); 22 | } 23 | ); 24 | } 25 | 26 | logout() { 27 | localStorage.clear(); 28 | this.router.navigate(['Login']); 29 | } 30 | 31 | registration(user) { 32 | let body = JSON.stringify({user: user}); 33 | this.http.post('/api/users', body, { headers: this.headers }).subscribe( 34 | response => { 35 | localStorage.setItem('token', response.json().token); 36 | this.router.navigate(['Tasks']); 37 | }, 38 | error => { 39 | alert(error.text()); 40 | } 41 | ); 42 | } 43 | 44 | isCurrentUser() { 45 | if(!localStorage.getItem('token')) { 46 | this.router.navigate(['Login']); 47 | return false; 48 | } 49 | return true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /frontend/www/services/task.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from 'angular2/core'; 2 | import {Headers} from 'angular2/http'; 3 | import {AuthHttp} from 'angular2-jwt/angular2-jwt'; 4 | import {Observable} from 'rxjs/Observable'; 5 | import {Observer} from 'rxjs/Observer'; 6 | import 'rxjs/add/operator/share'; 7 | import 'rxjs/add/operator/map'; 8 | import {Task} from '../models/task'; 9 | 10 | @Injectable() 11 | export class TaskService { 12 | tasks$: Observable; 13 | private _tasksObserver: Observer; 14 | private _dataStore: { 15 | tasks: Task[]; 16 | }; 17 | private headers; 18 | 19 | constructor(private authHttp: AuthHttp) { 20 | this.headers = new Headers({'Content-Type': 'application/json'}); 21 | this.tasks$ = new Observable(observer => 22 | this._tasksObserver = observer).share(); 23 | this._dataStore = { tasks: [] }; 24 | } 25 | 26 | load() { 27 | this.authHttp.get('/api/tasks').map(response => response.json()).subscribe(data => { 28 | this._dataStore.tasks = data; 29 | this._tasksObserver.next(this._dataStore.tasks); 30 | }, error => console.log('Could not load tasks.')); 31 | } 32 | 33 | create(task) { 34 | this.authHttp.post('/api/tasks', JSON.stringify({task: task}), { headers: this.headers }) 35 | .map(response => response.json()).subscribe(data => { 36 | this._dataStore.tasks.unshift(data); 37 | this._tasksObserver.next(this._dataStore.tasks); 38 | }, error => console.log('Could not create task.')); 39 | } 40 | 41 | update(task) { 42 | this.authHttp.put(`/api/tasks/${task.id}`, JSON.stringify({task: task}), { headers: this.headers }) 43 | .map(response => response.json()).subscribe(data => { 44 | this._dataStore.tasks.forEach((task, i) => { 45 | if (task.id === data.id) { this._dataStore.tasks[i] = data; } 46 | }); 47 | 48 | this._tasksObserver.next(this._dataStore.tasks); 49 | }, error => console.log('Could not update task.')); 50 | } 51 | 52 | delete(taskId: number) { 53 | this.authHttp.delete(`/api/tasks/${taskId}`).subscribe(response => { 54 | this._dataStore.tasks.forEach((t, index) => { 55 | if (t.id === taskId) { this._dataStore.tasks.splice(index, 1); } 56 | }); 57 | 58 | this._tasksObserver.next(this._dataStore.tasks); 59 | }, error => console.log('Could not delete task.')); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /frontend/www/sw.js: -------------------------------------------------------------------------------- 1 | // Cache then network strategy 2 | // https://jakearchibald.com/2014/offline-cookbook/#cache-then-network 3 | self.addEventListener('fetch', function(event) { 4 | event.respondWith( 5 | caches.open('angular2-seed').then(function(cache) { 6 | return fetch(event.request).then(function(response) { 7 | cache.put(event.request, response.clone()); 8 | return response; 9 | }); 10 | }) 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/www/templates/app.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /frontend/www/templates/login.html: -------------------------------------------------------------------------------- 1 |
2 | 15 |
16 | -------------------------------------------------------------------------------- /frontend/www/templates/navbar.html: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /frontend/www/templates/registration.html: -------------------------------------------------------------------------------- 1 |
2 | 24 |
25 | -------------------------------------------------------------------------------- /frontend/www/templates/tasks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | 6 |
7 |
8 |

Create a new task

9 | 10 |
11 |
12 | -------------------------------------------------------------------------------- /frontend/www/templates/tasks/form.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 |
7 | 8 | 9 |
10 | 11 |
12 | -------------------------------------------------------------------------------- /frontend/www/templates/tasks/list.html: -------------------------------------------------------------------------------- 1 |

My tasks

2 |

Active

3 |
    4 |
  • 5 | 6 |

    {{task.name}}

    7 | 8 | 9 |

    {{task.description}}

    10 | 11 |
    12 | 13 | 14 |
    15 |
    16 | 20 | 21 | 22 |
    23 |
  • 24 |
25 | 26 |

Done

27 |
    28 |
  • 29 | 30 |

    {{task.name}}

    31 | 32 |

    {{task.description}}

    33 |
    34 | 38 | 39 |
    40 |
  • 41 |
42 | --------------------------------------------------------------------------------