diff --git a/tickets/Gemfile.lock b/tickets/Gemfile.lock
index 4df3920..239a959 100644
--- a/tickets/Gemfile.lock
+++ b/tickets/Gemfile.lock
@@ -41,7 +41,7 @@ GEM
bcrypt (3.1.10)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
- builder (3.2.2)
+ builder (3.2.3)
byebug (3.5.1)
columnize (~> 0.8)
debugger-linecache (~> 1.2)
@@ -55,6 +55,8 @@ GEM
coffee-script-source (1.9.1)
columnize (0.9.0)
commonjs (0.2.7)
+ concurrent-ruby (1.1.5)
+ crass (1.0.5)
debug_inspector (0.0.2)
debugger-linecache (1.2.0)
erubis (2.7.0)
@@ -64,7 +66,8 @@ GEM
globalid (0.3.3)
activesupport (>= 4.1.0)
hike (1.2.3)
- i18n (0.7.0)
+ i18n (0.9.5)
+ concurrent-ruby (~> 1.0)
jbuilder (2.2.11)
activesupport (>= 3.0.0, < 5)
multi_json (~> 1.2)
@@ -72,7 +75,7 @@ GEM
rails-dom-testing (~> 1.0)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
- json (1.8.2)
+ json (1.8.6)
jwt (1.3.0)
less (2.6.0)
commonjs (~> 0.2.7)
@@ -80,18 +83,19 @@ GEM
actionpack (>= 3.1)
less (~> 2.6.0)
libv8 (3.16.14.7)
- loofah (2.0.1)
+ loofah (2.3.1)
+ crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.6.3)
mime-types (>= 1.16, < 3)
mime-types (2.4.3)
- mini_portile (0.6.2)
- minitest (5.5.1)
+ mini_portile2 (2.4.0)
+ minitest (5.13.0)
multi_json (1.11.0)
multi_xml (0.5.5)
multipart-post (2.0.0)
- nokogiri (1.6.6.2)
- mini_portile (~> 0.6.0)
+ nokogiri (1.10.5)
+ mini_portile2 (~> 2.4.0)
oauth (0.4.7)
oauth2 (1.0.0)
faraday (>= 0.8, < 0.10)
@@ -100,7 +104,7 @@ GEM
multi_xml (~> 0.5)
rack (~> 1.2)
pg (0.18.1)
- rack (1.6.0)
+ rack (1.6.11)
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.0)
@@ -116,12 +120,12 @@ GEM
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
- rails-dom-testing (1.0.5)
- activesupport (>= 4.2.0.beta, < 5.0)
- nokogiri (~> 1.6.0)
+ rails-dom-testing (1.0.9)
+ activesupport (>= 4.2.0, < 5.0)
+ nokogiri (~> 1.6)
rails-deprecated_sanitizer (>= 1.0.1)
- rails-html-sanitizer (1.0.1)
- loofah (~> 2.0)
+ rails-html-sanitizer (1.3.0)
+ loofah (~> 2.3)
railties (4.2.0)
actionpack (= 4.2.0)
activesupport (= 4.2.0)
@@ -163,7 +167,7 @@ GEM
libv8 (~> 3.16.14.0)
ref
thor (0.19.1)
- thread_safe (0.3.4)
+ thread_safe (0.3.6)
tilt (1.4.1)
transitions (0.2.0)
turbolinks (2.5.3)
@@ -173,7 +177,7 @@ GEM
execjs (~> 2.2)
rails (~> 4.1)
railties (~> 4.1)
- tzinfo (1.2.2)
+ tzinfo (1.2.5)
thread_safe (~> 0.1)
uglifier (2.7.1)
execjs (>= 0.3.0)
diff --git a/tpt_test_task/.gitignore b/tpt_test_task/.gitignore
new file mode 100644
index 0000000..4e9ae53
--- /dev/null
+++ b/tpt_test_task/.gitignore
@@ -0,0 +1,29 @@
+# See https://help.github.com/articles/ignoring-files for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+# git config --global core.excludesfile '~/.gitignore_global'
+
+# Ignore bundler config.
+/.bundle
+
+# Ignore all logfiles and tempfiles.
+/log/*
+/tmp/*
+!/log/.keep
+!/tmp/.keep
+
+# Ignore uploaded files in development
+/storage/*
+
+.byebug_history
+
+# Ignore master key for decrypting credentials and more.
+/config/master.key
+
+.DS_Store
+
+# Ignore application configuration
+/config/application.yml
+
+node_modules
diff --git a/tpt_test_task/.rspec b/tpt_test_task/.rspec
new file mode 100644
index 0000000..c99d2e7
--- /dev/null
+++ b/tpt_test_task/.rspec
@@ -0,0 +1 @@
+--require spec_helper
diff --git a/tpt_test_task/.ruby-gemset b/tpt_test_task/.ruby-gemset
new file mode 100644
index 0000000..162d717
--- /dev/null
+++ b/tpt_test_task/.ruby-gemset
@@ -0,0 +1 @@
+oleg-gorbunov
diff --git a/tpt_test_task/.ruby-version b/tpt_test_task/.ruby-version
new file mode 100644
index 0000000..4fd0fe3
--- /dev/null
+++ b/tpt_test_task/.ruby-version
@@ -0,0 +1 @@
+2.5.1
\ No newline at end of file
diff --git a/tpt_test_task/Gemfile b/tpt_test_task/Gemfile
new file mode 100644
index 0000000..18bdb9b
--- /dev/null
+++ b/tpt_test_task/Gemfile
@@ -0,0 +1,39 @@
+source 'https://rubygems.org'
+git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
+ruby '2.5.1'
+
+gem 'rails', '~> 5.2.0'
+gem 'pg', '>= 0.18', '< 2.0'
+gem 'puma', '~> 3.11'
+
+gem 'active_model_serializers', '~> 0.10.0'
+
+# Use ActiveModel has_secure_password
+# gem 'bcrypt', '~> 3.1.7'
+
+gem 'bootsnap', '>= 1.1.0', require: false
+gem 'rack-cors'
+gem 'sorcery'
+gem 'interactor', '~> 3.0'
+gem 'jwt'
+gem 'figaro'
+gem 'rolify'
+gem 'pundit'
+gem 'faker'
+
+group :development, :test do
+ gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
+ gem 'awesome_print'
+
+ gem 'rspec-rails', '~> 3.7'
+ gem 'factory_bot_rails'
+end
+
+group :development do
+ gem 'listen', '>= 3.0.5', '< 3.2'
+ gem 'spring'
+ gem 'spring-watcher-listen', '~> 2.0.0'
+end
+
+gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
diff --git a/tpt_test_task/Gemfile.lock b/tpt_test_task/Gemfile.lock
new file mode 100644
index 0000000..27a402c
--- /dev/null
+++ b/tpt_test_task/Gemfile.lock
@@ -0,0 +1,221 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ actioncable (5.2.0)
+ actionpack (= 5.2.0)
+ nio4r (~> 2.0)
+ websocket-driver (>= 0.6.1)
+ actionmailer (5.2.0)
+ actionpack (= 5.2.0)
+ actionview (= 5.2.0)
+ activejob (= 5.2.0)
+ mail (~> 2.5, >= 2.5.4)
+ rails-dom-testing (~> 2.0)
+ actionpack (5.2.0)
+ actionview (= 5.2.0)
+ activesupport (= 5.2.0)
+ rack (~> 2.0)
+ rack-test (>= 0.6.3)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
+ actionview (5.2.0)
+ activesupport (= 5.2.0)
+ builder (~> 3.1)
+ erubi (~> 1.4)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
+ active_model_serializers (0.10.7)
+ actionpack (>= 4.1, < 6)
+ activemodel (>= 4.1, < 6)
+ case_transform (>= 0.2)
+ jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
+ activejob (5.2.0)
+ activesupport (= 5.2.0)
+ globalid (>= 0.3.6)
+ activemodel (5.2.0)
+ activesupport (= 5.2.0)
+ activerecord (5.2.0)
+ activemodel (= 5.2.0)
+ activesupport (= 5.2.0)
+ arel (>= 9.0)
+ activestorage (5.2.0)
+ actionpack (= 5.2.0)
+ activerecord (= 5.2.0)
+ marcel (~> 0.3.1)
+ activesupport (5.2.0)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ arel (9.0.0)
+ awesome_print (1.8.0)
+ bcrypt (3.1.12)
+ bootsnap (1.3.0)
+ msgpack (~> 1.0)
+ builder (3.2.3)
+ byebug (10.0.2)
+ case_transform (0.2)
+ activesupport
+ concurrent-ruby (1.0.5)
+ crass (1.0.4)
+ diff-lcs (1.3)
+ erubi (1.7.1)
+ factory_bot (4.10.0)
+ activesupport (>= 3.0.0)
+ factory_bot_rails (4.10.0)
+ factory_bot (~> 4.10.0)
+ railties (>= 3.0.0)
+ faker (1.8.7)
+ i18n (>= 0.7)
+ faraday (0.12.2)
+ multipart-post (>= 1.2, < 3)
+ ffi (1.9.25)
+ figaro (1.1.1)
+ thor (~> 0.14)
+ globalid (0.4.1)
+ activesupport (>= 4.2.0)
+ i18n (1.0.1)
+ concurrent-ruby (~> 1.0)
+ interactor (3.1.1)
+ jsonapi-renderer (0.2.0)
+ jwt (1.5.6)
+ listen (3.1.5)
+ rb-fsevent (~> 0.9, >= 0.9.4)
+ rb-inotify (~> 0.9, >= 0.9.7)
+ ruby_dep (~> 1.2)
+ loofah (2.2.2)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ mail (2.7.0)
+ mini_mime (>= 0.1.1)
+ marcel (0.3.2)
+ mimemagic (~> 0.3.2)
+ method_source (0.9.0)
+ mimemagic (0.3.2)
+ mini_mime (1.0.0)
+ mini_portile2 (2.3.0)
+ minitest (5.11.3)
+ msgpack (1.2.4)
+ multi_json (1.13.1)
+ multi_xml (0.6.0)
+ multipart-post (2.0.0)
+ nio4r (2.3.1)
+ nokogiri (1.8.2)
+ mini_portile2 (~> 2.3.0)
+ oauth (0.5.4)
+ oauth2 (1.4.0)
+ faraday (>= 0.8, < 0.13)
+ jwt (~> 1.0)
+ multi_json (~> 1.3)
+ multi_xml (~> 0.5)
+ rack (>= 1.2, < 3)
+ pg (1.0.0)
+ puma (3.11.4)
+ pundit (1.1.0)
+ activesupport (>= 3.0.0)
+ rack (2.0.5)
+ rack-cors (1.0.2)
+ rack-test (1.0.0)
+ rack (>= 1.0, < 3)
+ rails (5.2.0)
+ actioncable (= 5.2.0)
+ actionmailer (= 5.2.0)
+ actionpack (= 5.2.0)
+ actionview (= 5.2.0)
+ activejob (= 5.2.0)
+ activemodel (= 5.2.0)
+ activerecord (= 5.2.0)
+ activestorage (= 5.2.0)
+ activesupport (= 5.2.0)
+ bundler (>= 1.3.0)
+ railties (= 5.2.0)
+ sprockets-rails (>= 2.0.0)
+ rails-dom-testing (2.0.3)
+ activesupport (>= 4.2.0)
+ nokogiri (>= 1.6)
+ rails-html-sanitizer (1.0.4)
+ loofah (~> 2.2, >= 2.2.2)
+ railties (5.2.0)
+ actionpack (= 5.2.0)
+ activesupport (= 5.2.0)
+ method_source
+ rake (>= 0.8.7)
+ thor (>= 0.18.1, < 2.0)
+ rake (12.3.1)
+ rb-fsevent (0.10.3)
+ rb-inotify (0.9.10)
+ ffi (>= 0.5.0, < 2)
+ rolify (5.2.0)
+ rspec-core (3.7.1)
+ rspec-support (~> 3.7.0)
+ rspec-expectations (3.7.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.7.0)
+ rspec-mocks (3.7.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.7.0)
+ rspec-rails (3.7.2)
+ actionpack (>= 3.0)
+ activesupport (>= 3.0)
+ railties (>= 3.0)
+ rspec-core (~> 3.7.0)
+ rspec-expectations (~> 3.7.0)
+ rspec-mocks (~> 3.7.0)
+ rspec-support (~> 3.7.0)
+ rspec-support (3.7.1)
+ ruby_dep (1.5.0)
+ sorcery (0.12.0)
+ bcrypt (~> 3.1)
+ oauth (~> 0.4, >= 0.4.4)
+ oauth2 (~> 1.0, >= 0.8.0)
+ spring (2.0.2)
+ activesupport (>= 4.2)
+ spring-watcher-listen (2.0.1)
+ listen (>= 2.7, < 4.0)
+ spring (>= 1.2, < 3.0)
+ sprockets (3.7.1)
+ concurrent-ruby (~> 1.0)
+ rack (> 1, < 3)
+ sprockets-rails (3.2.1)
+ actionpack (>= 4.0)
+ activesupport (>= 4.0)
+ sprockets (>= 3.0.0)
+ thor (0.20.0)
+ thread_safe (0.3.6)
+ tzinfo (1.2.5)
+ thread_safe (~> 0.1)
+ websocket-driver (0.7.0)
+ websocket-extensions (>= 0.1.0)
+ websocket-extensions (0.1.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ active_model_serializers (~> 0.10.0)
+ awesome_print
+ bootsnap (>= 1.1.0)
+ byebug
+ factory_bot_rails
+ faker
+ figaro
+ interactor (~> 3.0)
+ jwt
+ listen (>= 3.0.5, < 3.2)
+ pg (>= 0.18, < 2.0)
+ puma (~> 3.11)
+ pundit
+ rack-cors
+ rails (~> 5.2.0)
+ rolify
+ rspec-rails (~> 3.7)
+ sorcery
+ spring
+ spring-watcher-listen (~> 2.0.0)
+ tzinfo-data
+
+RUBY VERSION
+ ruby 2.5.1p57
+
+BUNDLED WITH
+ 1.16.2
diff --git a/tpt_test_task/README.md b/tpt_test_task/README.md
new file mode 100644
index 0000000..f084a14
--- /dev/null
+++ b/tpt_test_task/README.md
@@ -0,0 +1,49 @@
+# Tracker
+
+test task
+
+1. what is the scenario by which user-manager and user-admin can appear in application?
+
+2. "If on a particular date a user has worked under the PreferredWorkingHourPerDay, these rows are red, otherwise green."
+
+here is my current understanding of this functionality. please correct me where I am wrong:
+
+example 1
+
+PreferredWorkingHourPerDay = 8
+
+June 9 records:
+record 1: 4h - should be green
+record 2: 4h - should be green
+record 3: 1h - should be red
+
+example 2
+
+PreferredWorkingHourPerDay = 8
+
+June 9 records:
+record 1: 4h - should be green
+record 2: 3h - should be green
+record 3: 2h - should be red
+
+example 3
+
+PreferredWorkingHourPerDay = 0
+
+June 9 records:
+record 1: 4h - should be red
+record 2: 3h - should be red
+record 3: 2h - should be red
+
+example 4
+
+PreferredWorkingHourPerDay not specified
+
+June 9 records:
+record 1: 4h - should be green
+record 2: 3h - should be green
+record 3: 2h - should be green
+
+3. are there any validation that should be applied for PreferredWorkingHourPerDay?
+
+4. do we need this application to be deployed somewhere?
diff --git a/tpt_test_task/Rakefile b/tpt_test_task/Rakefile
new file mode 100644
index 0000000..e85f913
--- /dev/null
+++ b/tpt_test_task/Rakefile
@@ -0,0 +1,6 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require_relative 'config/application'
+
+Rails.application.load_tasks
diff --git a/tpt_test_task/_frontend/.eslintrc.js b/tpt_test_task/_frontend/.eslintrc.js
new file mode 100644
index 0000000..6393052
--- /dev/null
+++ b/tpt_test_task/_frontend/.eslintrc.js
@@ -0,0 +1,77 @@
+const OFF = 0
+const WARN = 1
+const ERROR = 2
+
+module.exports = {
+ 'env': {
+ 'browser': true,
+ 'commonjs': true,
+ 'es6': true
+ },
+ 'parserOptions': {
+ 'ecmaFeatures': {
+ 'experimentalObjectRestSpread': true,
+ 'jsx': true
+ },
+ 'sourceType': 'module'
+ },
+ 'parser': 'babel-eslint',
+ 'plugins': ['react', 'flowtype'],
+ 'extends': ['airbnb', 'plugin:flowtype/recommended'],
+ 'settings': {
+ 'import/resolver': 'webpack'
+ },
+ 'rules': {
+ 'linebreak-style': [ERROR, 'unix'],
+ 'jsx-quotes': [ERROR, 'prefer-single'],
+ 'quotes': [ERROR, 'single', { 'allowTemplateLiterals': true }],
+ 'quote-props': [ERROR, 'as-needed'],
+ 'semi': OFF,
+ 'no-unused-vars': [ERROR, { 'vars': 'all', 'args': 'after-used', 'argsIgnorePattern': '^_' }],
+ 'comma-spacing': OFF,
+ 'comma-dangle': OFF,
+ 'object-curly-spacing': OFF,
+ 'arrow-parens': OFF,
+ 'new-cap': OFF,
+ 'spaced-comment': OFF,
+ 'no-confusing-arrow': OFF,
+ 'object-property-newline': OFF,
+ 'no-console': OFF,
+ 'camelcase': OFF,
+ 'default-case': OFF,
+ 'prefer-template': OFF,
+ 'max-len': OFF,
+ 'no-mixed-operators': OFF,
+ 'object-curly-newline': OFF,
+ 'no-param-reassign': OFF,
+ 'function-paren-newline': OFF,
+ 'no-multiple-empty-lines': OFF,
+ 'consistent-return': OFF,
+ 'no-prototype-builtins': OFF,
+ 'no-unused-expressions': OFF,
+ 'no-underscore-dangle': OFF,
+ 'class-methods-use-this': OFF,
+ 'arrow-body-style': OFF,
+ 'no-else-return': OFF,
+ 'import/no-named-as-default': OFF,
+ 'import/newline-after-import': OFF,
+ 'import/extensions': OFF,
+ 'import/prefer-default-export': OFF,
+ 'react/prefer-stateless-function': OFF,
+ 'react/forbid-prop-types': OFF,
+ 'react/no-unused-prop-types': OFF,
+ 'react/jsx-space-before-closing': OFF,
+ 'react/jsx-first-prop-new-line': OFF,
+ 'react/jsx-boolean-value': OFF,
+ 'react/sort-comp': OFF,
+ 'react/jsx-closing-bracket-location': OFF,
+ 'react/jsx-wrap-multilines': OFF,
+ 'react/jsx-closing-tag-location': OFF,
+ 'react/jsx-filename-extension': [ERROR, { 'extensions': ['.js'] }],
+ 'react/jsx-max-props-per-line': OFF,
+ 'jsx-a11y/no-static-element-interactions': OFF,
+ 'jsx-a11y/anchor-is-valid': OFF
+ }
+}
+
+
diff --git a/tpt_test_task/_frontend/.flowconfig b/tpt_test_task/_frontend/.flowconfig
new file mode 100644
index 0000000..03105f5
--- /dev/null
+++ b/tpt_test_task/_frontend/.flowconfig
@@ -0,0 +1,13 @@
+[ignore]
+.*/node_modules/styled-components.*
+.*/node_modules/eslint-plugin-jsx-a11y.*
+.*/node_modules/editions/es2015.*
+
+[include]
+
+[libs]
+
+[options]
+module.system.node.resolve_dirname=app
+
+suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore
diff --git a/tpt_test_task/_frontend/.gitignore b/tpt_test_task/_frontend/.gitignore
new file mode 100644
index 0000000..224a875
--- /dev/null
+++ b/tpt_test_task/_frontend/.gitignore
@@ -0,0 +1,8 @@
+node_modules
+.DS_Store
+/dist/styles.css
+/dist/bundle.js
+/dist/0.bundle.js
+npm-debug.log*
+yarn-error.log*
+flow-typed/npm
diff --git a/tpt_test_task/_frontend/README.md b/tpt_test_task/_frontend/README.md
new file mode 100644
index 0000000..e269fd8
--- /dev/null
+++ b/tpt_test_task/_frontend/README.md
@@ -0,0 +1 @@
+# Tracker
diff --git a/tpt_test_task/_frontend/app/Config.js b/tpt_test_task/_frontend/app/Config.js
new file mode 100644
index 0000000..2ad1d50
--- /dev/null
+++ b/tpt_test_task/_frontend/app/Config.js
@@ -0,0 +1,30 @@
+//@flow
+const serverUrl = process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : 'https://oleg-tracker.herokuapp.com'
+export const api = {
+ serverUrl,
+ url: `${serverUrl}/`,
+}
+
+export const routes = {
+ users: {
+ index: () => `v1/users`,
+ me: () => `v1/users/me`,
+ delete: (id: number) => `v1/users/${id}`,
+ update: (id: number) => `v1/users/${id}`,
+ show: (id: number) => `v1/users/${id}`,
+ },
+ activities: {
+ index: () => `v1/activities`,
+ create: () => `v1/activities`,
+ delete: (id: number) => `v1/activities/${id}`,
+ update: (id: number) => `v1/activities/${id}`,
+ show: (id: number) => `v1/activities/${id}`,
+ },
+ auth: {
+ signin: () => `v1/login`,
+ signup: () => `v1/signup`,
+ },
+}
+
+const config = { api, routes }
+export default config
diff --git a/tpt_test_task/_frontend/app/Root.js b/tpt_test_task/_frontend/app/Root.js
new file mode 100644
index 0000000..2be5d8f
--- /dev/null
+++ b/tpt_test_task/_frontend/app/Root.js
@@ -0,0 +1,54 @@
+//@flow
+import React from 'react'
+import { render } from 'react-dom'
+import { browserHistory, Router, Route, Redirect } from 'react-router'
+import { Provider } from 'react-redux'
+import { authenticate } from 'reducers/Saga'
+
+import Layout from 'containers/layout/Layout'
+import Signin from 'containers/pages/auth/signin/Signin'
+import Signup from 'containers/pages/auth/signup/Signup'
+import UserList from 'containers/pages/users/UserList'
+import NewUser from 'containers/pages/users/NewUser'
+import EditUser from 'containers/pages/users/EditUser'
+import ActivityList from 'containers/pages/activities/ActivityList'
+import NewActivity from 'containers/pages/activities/NewActivity'
+import EditActivity from 'containers/pages/activities/EditActivity'
+
+import authorize from 'containers/pages/auth/authorizer/Authorizer'
+import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
+
+import store from 'Store'
+import 'assets/styles/global.css'
+
+import moment from 'moment'
+import momentDurationFormatSetup from 'moment-duration-format'
+momentDurationFormatSetup(moment)
+
+const callback = () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ // $FlowIgnore
+ document.getElementById('root')
+ )
+}
+
+store.dispatch(authenticate(callback))
diff --git a/tpt_test_task/_frontend/app/Store.js b/tpt_test_task/_frontend/app/Store.js
new file mode 100644
index 0000000..ee8d244
--- /dev/null
+++ b/tpt_test_task/_frontend/app/Store.js
@@ -0,0 +1,18 @@
+//@flow
+import { createStore, applyMiddleware, compose } from 'redux'
+import reducer from 'reducers'
+import { createLogger } from 'redux-logger'
+import createSagaMiddleware from 'redux-saga'
+import sagas from 'sagas'
+
+const sagaMiddleware = createSagaMiddleware()
+const logger = createLogger({
+ collapsed: true,
+ actionTransformer: (action) => ({type: action.type, ...action.payload})
+})
+
+const enhancer = compose(applyMiddleware(sagaMiddleware, logger))
+const store = createStore(reducer, undefined, enhancer)
+sagaMiddleware.run(sagas, store.dispatch)
+
+export default store
diff --git a/tpt_test_task/_frontend/app/Theme.js b/tpt_test_task/_frontend/app/Theme.js
new file mode 100644
index 0000000..dfe1f37
--- /dev/null
+++ b/tpt_test_task/_frontend/app/Theme.js
@@ -0,0 +1,10 @@
+//@flow
+
+// use http://chir.ag/projects/name-that-color to name colors
+export const palette = {
+ primary: '#00bdd4',
+ secondary: '#ff4081',
+ pacificBlue: '#00acc1',
+ red: '#ff0000',
+ silverChalice: '#aaaaaa',
+}
diff --git a/tpt_test_task/_frontend/app/Types.js b/tpt_test_task/_frontend/app/Types.js
new file mode 100644
index 0000000..a8e8631
--- /dev/null
+++ b/tpt_test_task/_frontend/app/Types.js
@@ -0,0 +1,3 @@
+// @flow
+export type Action = {type: string, payload: Object}
+export type MethodType = 'GET' | 'POST' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PUT' | 'PATCH'
diff --git a/tpt_test_task/_frontend/app/__blueprints/Component.js b/tpt_test_task/_frontend/app/__blueprints/Component.js
new file mode 100644
index 0000000..d672178
--- /dev/null
+++ b/tpt_test_task/_frontend/app/__blueprints/Component.js
@@ -0,0 +1,15 @@
+//@flow
+import React from 'react'
+import pt from 'prop-types'
+
+export default class Component extends React.Component<*> {
+ static propTypes = {
+ prop: pt.string.isRequired,
+ }
+
+ render() {
+ return
+ test
+
+ }
+}
diff --git a/tpt_test_task/_frontend/app/__blueprints/Container.js b/tpt_test_task/_frontend/app/__blueprints/Container.js
new file mode 100644
index 0000000..43b0aa7
--- /dev/null
+++ b/tpt_test_task/_frontend/app/__blueprints/Container.js
@@ -0,0 +1,13 @@
+//@flow
+import { connect } from 'react-redux'
+import List from 'components/layout/Layout'
+
+export const mapStateToProps = (_state: Object): Object => ({
+
+})
+
+export const mapDispatchToProps = (_dispatch: Function): Object => ({
+
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(List)
diff --git a/tpt_test_task/_frontend/app/api/Activity.js b/tpt_test_task/_frontend/app/api/Activity.js
new file mode 100644
index 0000000..512fbfa
--- /dev/null
+++ b/tpt_test_task/_frontend/app/api/Activity.js
@@ -0,0 +1,23 @@
+//@flow
+import { routes } from 'Config'
+import { apiGet, apiPost, apiDelete, apiPut } from 'api/__helpers'
+
+export function fetchActivities(params: Object = {}) {
+ return apiGet(routes.activities.index(), params)
+}
+
+export function fetchActivity(id: number) {
+ return apiGet(routes.activities.show(id))
+}
+
+export function createActivity(params: Object) {
+ return apiPost(routes.activities.create(), params)
+}
+
+export function deleteActivity(id: number) {
+ return apiDelete(routes.activities.delete(id))
+}
+
+export function updateActivity(id: number, params: Object) {
+ return apiPut(routes.activities.update(id), params)
+}
diff --git a/tpt_test_task/_frontend/app/api/Auth.js b/tpt_test_task/_frontend/app/api/Auth.js
new file mode 100644
index 0000000..2cfe110
--- /dev/null
+++ b/tpt_test_task/_frontend/app/api/Auth.js
@@ -0,0 +1,10 @@
+//@flow
+import { routes } from 'Config'
+import { apiPost } from 'api/__helpers'
+
+export function signin(params: Object) {
+ return apiPost(routes.auth.signin(), params)
+}
+export function signup(params: Object) {
+ return apiPost(routes.auth.signup(), params)
+}
diff --git a/tpt_test_task/_frontend/app/api/User.js b/tpt_test_task/_frontend/app/api/User.js
new file mode 100644
index 0000000..d1622c3
--- /dev/null
+++ b/tpt_test_task/_frontend/app/api/User.js
@@ -0,0 +1,23 @@
+//@flow
+import { routes } from 'Config'
+import { apiGet, apiDelete, apiPut } from 'api/__helpers'
+
+export function fetchUsers() {
+ return apiGet(routes.users.index())
+}
+
+export function fetchUser(id: number) {
+ return apiGet(routes.users.show(id))
+}
+
+export function getMe() {
+ return apiGet(routes.users.me())
+}
+
+export function deleteUser(id: number) {
+ return apiDelete(routes.users.delete(id))
+}
+
+export function updateUser(id: number, params: Object) {
+ return apiPut(routes.users.update(id), params)
+}
diff --git a/tpt_test_task/_frontend/app/api/__helpers.js b/tpt_test_task/_frontend/app/api/__helpers.js
new file mode 100644
index 0000000..b8bda80
--- /dev/null
+++ b/tpt_test_task/_frontend/app/api/__helpers.js
@@ -0,0 +1,78 @@
+//@flow
+import 'whatwg-fetch'
+import { api } from 'Config'
+import type { MethodType } from 'Types'
+import Localstorage from 'services/Localstorage'
+import querystring from 'querystring'
+
+export function logResponse(response: Object) {
+ console.log(response)
+ return response
+}
+
+export function parseJSON(response: Object) {
+ return response.text().then(text => {
+ if (text === '') return new Promise((resolve,_reject) => { resolve({}) })
+
+ const json = JSON.parse(text)
+ return new Promise((resolve,_reject) => { resolve(json) })
+ })
+}
+
+function handleResponse(response: Object) {
+ return response
+ .then(logResponse)
+ .then(parseJSON)
+ .catch(e => {
+ if (e.response) throw e
+ console.log(e)
+ const error = new Error('Bad Connection')
+ error.message = 'Check your internet connection'
+ throw error
+ })
+}
+
+export function apiPostFormData(path: string, files: Array