diff --git a/Gemfile b/Gemfile index 25a0e7f..69a2ade 100644 --- a/Gemfile +++ b/Gemfile @@ -95,3 +95,4 @@ gem 'high_voltage', '~> 2.4.0' gem 'airbrake' gem 'bootstrap-sass', '~> 3.3.5' +gem 'react-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 0fe9c09..c2323aa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,6 +46,10 @@ GEM autoprefixer-rails (6.0.2) execjs json + babel-source (5.8.35) + babel-transpiler (0.7.0) + babel-source (>= 4.0, < 6) + execjs (~> 2.0) bcrypt (3.1.10) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) @@ -79,6 +83,7 @@ GEM execjs coffee-script-source (1.9.1.1) colorize (0.7.7) + connection_pool (2.2.1) coveralls (0.8.2) json (~> 1.8) rest-client (>= 1.6.8, < 2) @@ -205,6 +210,13 @@ GEM thor (>= 0.18.1, < 2.0) rake (10.4.2) rdoc (4.2.0) + react-rails (1.10.0) + babel-transpiler (>= 0.7.0) + coffee-script-source (~> 1.8) + connection_pool + execjs + railties (>= 3.2) + tilt responders (2.1.0) railties (>= 4.2.0, < 5) rest-client (1.8.0) @@ -313,6 +325,7 @@ DEPENDENCIES omniauth-oauth2 purecss-rails rails (= 4.2.4) + react-rails rspec-rails sass-rails (~> 5.0) sdoc (~> 0.4.0) @@ -324,4 +337,4 @@ DEPENDENCIES web-console (~> 2.0) BUNDLED WITH - 1.10.6 + 1.13.7 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index bc4b5a6..1de917d 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -12,12 +12,16 @@ // //= require jquery //= require jquery_ujs +//= require bootstrap-sprockets //= require dataTables/jquery.dataTables //= require dataTables/extras/dataTables.responsive //= require dataTables/jquery.dataTables //= require select2 //= require jquery-dateFormat //= require turbolinks +//= require react +//= require react_ujs +//= require components //= require_tree . ready = function() { diff --git a/app/assets/javascripts/components.js b/app/assets/javascripts/components.js new file mode 100644 index 0000000..9ce7a4f --- /dev/null +++ b/app/assets/javascripts/components.js @@ -0,0 +1 @@ +//= require_tree ./components diff --git a/app/assets/javascripts/components/.gitkeep b/app/assets/javascripts/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/javascripts/components/transaction_form.jsx.coffee b/app/assets/javascripts/components/transaction_form.jsx.coffee new file mode 100644 index 0000000..2aa6c24 --- /dev/null +++ b/app/assets/javascripts/components/transaction_form.jsx.coffee @@ -0,0 +1,213 @@ +{ button, div, form, h3, input, option, select } = React.DOM + +Action = React.createFactory React.createClass + buttonClass: (b) -> + { giving } = @props + c = ['btn', 'btn-default'] + c.push 'active' if b == giving + c.join ' ' + onClick: (b) -> + => + @props.setAction b + render: -> + { giving } = @props + div className: 'btn-group btn-group-lg', + button type: 'button', className: @buttonClass(true), onClick: @onClick(true), + 'Give Money' + button type: 'button', className: @buttonClass(false), onClick: @onClick(false), + 'Request Money' + +Amount = React.createFactory React.createClass + onChange: (ref) -> + @props.setAmount ref.target.value + format: (ref) -> + t = ref.target + t.value = parseFloat(t.value).toFixed(2) if t.value + render: -> + div className: 'row', + div className: 'col-xs-4', + div className: 'input-group', + div className: 'input-group-addon', '€' + input { + className: 'form-control input-lg', + name: 'transaction[euros]' + onBlur: @format, + onChange: @onChange, + placeholder: '0.00', + type: 'number', + } + +Peer = React.createFactory React.createClass + onChange: (ref) -> + @props.setPeer ref.target.value + options: -> + { peer, peers } = @props + if peer == '' or peers.includes(peer) + [] + else + re = new RegExp peer + peers.filter (s) -> + s.match(re) != null + inputClass: (n) -> + c = ['form-control', 'input-lg'] + c.push 'active' if n > 0 + c.join ' ' + setPeer: (p) -> + => + @props.setPeer p + render: -> + options = @options() + div className: 'row', + div className: 'col-xs-4', + div className: 'suggestions-wrapper', + input { + className: @inputClass(options.length), + onChange: @onChange, + placeholder: 'Zeus member', + type: 'text', + value: (@props.peer || '') + } + if options.length != 0 + div className: 'suggestions', + @options().map (s, i) => + div className: 'suggestion', key: i, onClick: @setPeer(s), + s + +Message = React.createFactory React.createClass + onChange: (ref) -> + @props.setMessage ref.target.value + render: -> + div className: 'row', + div className: 'col-xs-8', + input { + className: 'form-control input-lg', + name: 'transaction[message]', + onChange: @onChange, + placeholder: 'Message' + type: 'text', + } + +Submit = React.createFactory React.createClass + render: -> + { onClick } = @props + div className: 'row', + div className: 'col-xs-4 col-xs-offset-4', + button { + className: 'btn btn-default btn-lg btn-block', + onClick: onClick, + type: 'submit', + }, 'Confirm' + +Step = React.createFactory React.createClass + render: -> + { error } = @props + div className: 'form-step', + div className: 'form-step-counter', @props.step + div className: 'form-step-content', + div className: 'form-step-title', + @props.title, + div className: 'form-step-error', + error + div className: 'clear-both' + @props.children + div className: 'clear-both' + +@TransactionForm = React.createClass + getInitialState: -> + step: 1, giving: null, amount: null, peer: null, message: null + setAction: (b) -> + @setState giving: b + @setState step: 2 unless @state.step > 1 + setAmount: (a) -> + @setState amount: a + @setState step: 3 unless @state.step > 2 + setPeer: (p) -> + @setState peer: p + @setState step: 4 unless @state.step > 3 + setMessage: (m) -> + @setState message: m + @setState step: 5 unless @state.step > 4 + submit: (e) -> + e.preventDefault() + + { giving, peer } = @state + { user } = @props + + errors = @errors() + if Object.keys(errors).length != 0 + return + + if giving + debtor = user.name + creditor = peer + else + debtor = peer + creditor = user.name + + $('') + .attr('name', 'transaction[debtor]') + .attr('value', debtor) + .attr('type', 'hidden') + .appendTo(@refs.form) + $('') + .attr('name', 'transaction[creditor]') + .attr('value', creditor) + .attr('type', 'hidden') + .appendTo(@refs.form) + + @refs.form.submit() + errors: -> + { amount, giving, message, peer } = @state + { peers, user } = @props + + errors = {} + + errors['giving'] = 'Please select an action.' unless giving != null + + unless amount + errors['amount'] = 'Please fill in an amount.' + else if parseFloat(amount) <= 0 + errors['amount'] = 'Please fill in a positive number.' + + unless message && message != "" + errors['message'] = 'Please fill in a message.' + + unless peer && peers.includes(peer) && peer != user + errors['peer'] = 'Please select a valid Zeus member.' + + errors + render: -> + { step, amount, giving, message, peer } = @state + { peers } = @props + + errors = @errors() + + div id: 'transaction-form', + h3 null, 'Transfer some money' + form ref: 'form', action: '/transactions', acceptCharset: 'UTF-8', method: 'post', + Step step: 1, title: 'What do you want to do?', + Action giving: giving, setAction: @setAction + if step >= 2 + Step { + step: 2, + title: "How much do you want to #{if giving then 'give' else 'receive'}?", + error: errors['amount'] if step > 2 + }, + Amount setAmount: @setAmount + if step >= 3 + Step { + step: 3, + title: "Who do you want to #{if giving then 'give it to' else 'receive it from'}?", + error: errors['peer'] if step > 3 + }, + Peer peer: peer, peers: peers, setPeer: @setPeer + if step >= 4 + Step { + step: 4, + title: "Why do you want to #{if giving then 'give' else 'receive'} this?", + error: errors['message'] if step > 4 + }, + Message setMessage: @setMessage + if step >= 5 + Submit onClick: @submit + div className: 'clear-both' diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 1dd0534..fd2bfd5 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -21,6 +21,6 @@ @import "bootstrap-sprockets"; @import "bootstrap"; -body { - padding: 30px; +.clear-both { + clear: both; } diff --git a/app/assets/stylesheets/card.scss b/app/assets/stylesheets/card.scss new file mode 100644 index 0000000..4f3f737 --- /dev/null +++ b/app/assets/stylesheets/card.scss @@ -0,0 +1,17 @@ +.card-wrapper { + padding: 0 20px; + margin-bottom: 10px; + + .card { + box-shadow: 0 1px 3px rgba(0,0,0, 0.12), 0 1px 2px rgba(0,0,0, 0.24); + border-radius: 2px; + + h1, .h1, h2, .h2, h3, .h3 { + margin-top: 0; + } + } +} + +.padded { + padding: 10px; +} diff --git a/app/assets/stylesheets/landing.css.scss b/app/assets/stylesheets/landing.css.scss index e33c853..6225954 100644 --- a/app/assets/stylesheets/landing.css.scss +++ b/app/assets/stylesheets/landing.css.scss @@ -42,3 +42,76 @@ a.login-button { .shame-percentage { text-align: right } + +.transaction { + border-bottom: 1px solid #c7d0d5; + padding: 15px 10px; + font-size: 16px; + color: rgb(45, 54, 59); + + .transaction-calendar { + float: left; + text-align: center; + color: #adbac2; + line-height: 1.1; + margin-top: 0.125em; + + .transaction-day { + font-size: 1.125em; + display: block; + } + + .transaction-month { + font-size: 0.750em; + text-transform: uppercase; + display: block; + } + } + + .transaction-block { + padding-left: 3.500em; + + .transaction-block-l { + width: 75%; + float: left; + + .transaction-message { + margin: 0; + color: #0072ae; + font-size: 1em; + text-transform: uppercase; + } + + .transaction-issuer { + display: inline-block; + color: #90949c; + font-size: 70%; + } + } + + .transaction-block-r { + width: 25%; + float: left; + text-align: right; + } + } +} + +.request { + h4 { + margin: 0; + color: #0072ae; + font-size: 1em; + text-transform: uppercase; + } + +} + +.notification ,.request { + border-top: 1px solid #c7d0d5; + padding: 15px 10px; + + .actions { + text-align: right; + } +} diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss new file mode 100644 index 0000000..12fe2ec --- /dev/null +++ b/app/assets/stylesheets/layout.scss @@ -0,0 +1,7 @@ +#content { + padding: 30px; +} + +.info-message { + color: rgb(144, 148, 156); +} diff --git a/app/assets/stylesheets/menu.scss b/app/assets/stylesheets/menu.scss new file mode 100644 index 0000000..2d6512b --- /dev/null +++ b/app/assets/stylesheets/menu.scss @@ -0,0 +1,72 @@ +$background-color: #f3f3f3; +$border-color: #cfcfcf; +$color: #777; + +.menu { + width: 100%; + height: 70px; + background: $background-color; + border-bottom: 1px solid $border-color; + + .menu-item { + display: inline-block; + vertical-align: middle; + height: 100%; + padding: 20px; + color: $color; + font-size: 1.875rem; + line-height: 1.4; + font-weight: 300; + + &:hover { + text-decoration: none; + } + } + + .menu-heading { + font-weight: bold; + font-size: 20px; + } + + .menu-list { + display: inline-block; + border-left: 1px solid $border-color; + height: 100%; + margin: 0; + padding: 0; + + &.menu-right { + float: right; + + .menu-item { + &.menu-item-signout:hover { + background-color: red; + color: white; + } + + &:last-child { + border-right: 0; + } + } + } + + .menu-item { + border-right: 1px dotted $border-color; + + .badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + color: white; + line-height: 1; + vertical-align: baseline; + white-space: nowrap; + text-align: center; + background-color: $color; + border-radius: 10px; + } + } + } +} diff --git a/app/assets/stylesheets/transaction_form.scss b/app/assets/stylesheets/transaction_form.scss new file mode 100644 index 0000000..e92de6e --- /dev/null +++ b/app/assets/stylesheets/transaction_form.scss @@ -0,0 +1,93 @@ +$step-color: #b5b5b5;; +$step-padding-top: 3px; +$step-width: 26px; + +#transaction-form { + .form-step { + margin-bottom: 10px; + + .form-step-counter { + float: left; + font-size: 16px; + font-weight: bold; + vertical-align: middle; + white-space: nowrap; + text-align: center; + display: inline-block; + min-width: 10px; + padding: $step-padding-top 7px; + height: $step-width; + width: $step-width; + background: $step-color; + color: #fff; + position: relative; + border-radius: 50%; + } + + .form-step-content { + margin-left: $step-width + 10px; + width: 100%; + + .form-step-title { + display: inline-block; + font-weight: bold; + margin-bottom: 5px; + } + + .form-step-error { + color: red; + font-size: 11px; + height: 15px; + } + + .form-options { + & > div { + border: 1px solid #000; + display: inline-block; + width: 40%; + margin-right: 10px; + padding: 20px; + text-align: center; + } + } + + .suggestions-wrapper { + position: relative; + + input.active { + border-radius: 6px 6px 0 0; + } + + .suggestions { + position: absolute; + top: 100%; + width: 100%; + border: 1px solid #ccc; + border-top: 0; + border-radius: 0 0 6px 6px; + background: #fff; + z-index: 2; + + .suggestion { + cursor: pointer; + padding: 5px 8px; + + &:hover { + background-color: #f7f7f7; + } + + &:last-child { + border-radius: 0 0 6px 6px; + } + } + } + } + + .btn { + &:focus, &:active { + outline: 0; + } + } + } + } +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9c9b8f2..19d333d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -27,6 +27,6 @@ class ApplicationController < ActionController::Base end def after_sign_in_path_for(resource) - current_user + root_path end end diff --git a/app/controllers/concerns/transactions_query.rb b/app/controllers/concerns/transactions_query.rb index bd87edc..2311809 100644 --- a/app/controllers/concerns/transactions_query.rb +++ b/app/controllers/concerns/transactions_query.rb @@ -6,7 +6,7 @@ class TransactionsQuery @transactions = Arel::Table.new(:transactions) @perspectived = Arel::Table.new(:perspectived_transactions) @peers = Arel::Table.new(:users).alias('peers') - @arel_table = Arel::Table.new(@user.name.concat('_transactions')) + @arel_table = Arel::Table.new("#{@user.name}_transactions") end def query diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 9f86424..8978823 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -10,7 +10,7 @@ class NotificationsController < ApplicationController def read @notification.read! - redirect_to user_notifications_path(@notification.user) + redirect_to root_path end private diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 42b9a09..c9f0efd 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -1,8 +1,15 @@ class PagesController < ApplicationController - require 'statistics' def landing + query = TransactionsQuery.new(current_user) + @transactions = ActiveRecord::Base.connection.exec_query(query.query.order(query.arel_table[:time].desc).take(10).project(Arel.star).to_sql) + @requests = current_user.incoming_requests.open.includes(:creditor).take(10) + @outgoing_requests = current_user.outgoing_requests.open.includes(:debtor).take(10) + @notifications = current_user.notifications.unread + end + + def sign_in_page @statistics = Statistics.new end end diff --git a/app/controllers/requests_controller.rb b/app/controllers/requests_controller.rb index 57d68ad..f2c5ac5 100644 --- a/app/controllers/requests_controller.rb +++ b/app/controllers/requests_controller.rb @@ -10,12 +10,12 @@ class RequestsController < ApplicationController def confirm @request.confirm! - redirect_to user_requests_path(@request.debtor) + redirect_to root_path end def decline @request.decline! - redirect_to user_requests_path(@request.debtor) + redirect_to root_path end private diff --git a/app/controllers/transactions_controller.rb b/app/controllers/transactions_controller.rb index d4d1a45..4227c7e 100644 --- a/app/controllers/transactions_controller.rb +++ b/app/controllers/transactions_controller.rb @@ -10,21 +10,21 @@ class TransactionsController < ApplicationController @transaction = Transaction.new(transaction_params) @transaction.reverse if @transaction.amount < 0 - if can? :create, @transaction - if @transaction.save - render json: @transaction, status: :created - else - render json: @transaction.errors.full_messages, - status: :unprocessable_entity + unless can? :create, @transaction + @transaction = Request.new @transaction.info + authorize!(:create, @transaction) + end + + if @transaction.save + respond_to do |format| + format.html { redirect_to root_path } + format.json { render json: @transaction, status: :created } end else - request = Request.new @transaction.info - authorize!(:create, request) - if request.save - render json: request, status: :created - else - render json: request.errors.full_messages, - status: :unprocessable_entity + respond_to do |format| + format.html { redirect_to root_path } + format.json { render json: @transaction.errors.full_messages, + status: :unprocessable_entity } end end end diff --git a/app/models/concerns/base_transaction.rb b/app/models/concerns/base_transaction.rb index 31c11dc..80fb884 100644 --- a/app/models/concerns/base_transaction.rb +++ b/app/models/concerns/base_transaction.rb @@ -1,13 +1,16 @@ module BaseTransaction extend ActiveSupport::Concern include ActionView::Helpers::NumberHelper + include ApplicationHelper included do belongs_to :debtor, class_name: 'User' belongs_to :creditor, class_name: 'User' belongs_to :issuer, polymorphic: true - validates :amount, numericality: { greater_than: 0 } + validates :debtor, presence: true + validates :creditor, presence: true + validates :amount, numericality: { greater_than: 0 } validate :different_debtor_creditor end @@ -18,7 +21,7 @@ module BaseTransaction end def amount_f - number_to_currency amount/100.0, unit: '€' + euro_from_cents amount end private diff --git a/app/models/notification.rb b/app/models/notification.rb index a926d62..aed0e88 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -13,6 +13,8 @@ class Notification < ActiveRecord::Base belongs_to :user + scope :unread, -> { where read: false } + def read! update_attributes read: true end diff --git a/app/models/user.rb b/app/models/user.rb index df5aa07..a60aceb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -21,7 +21,7 @@ class User < ActiveRecord::Base has_many :incoming_requests, class_name: 'Request', foreign_key: 'debtor_id' has_many :outgoing_requests, - class_name: 'Request', foreign_key: 'debtor_id' + class_name: 'Request', foreign_key: 'creditor_id' has_many :notifications has_many :issued_transactions, as: :issuer, class_name: 'Transaction' diff --git a/app/views/application/_menu.html.haml b/app/views/application/_menu.html.haml index 09fe0ac..e558cc1 100644 --- a/app/views/application/_menu.html.haml +++ b/app/views/application/_menu.html.haml @@ -1,17 +1,22 @@ .pure-u-1 - .pure-menu.pure-menu-horizontal - = link_to "Tab", root_path, class: "pure-menu-heading pure-menu-link" - %ul.pure-menu-list - %li.pure-menu-item - = link_to current_user.name.capitalize, current_user, class: "pure-menu-link" - - if current_user.penning - %li.pure-menu-item - =link_to "Zeus", User.zeus, class: "pure-menu-link" - %li.pure-menu-item - = link_to "Requests (#{User.zeus.incoming_requests.size})", user_requests_path(User.zeus), class: 'pure-menu-link' - %li.pure-menu-item - = link_to "Notifications (#{User.zeus.notifications.size})", user_notifications_path(User.zeus), class: 'pure-menu-link' - %li.pure-menu-item - = link_to "Requests (#{current_user.incoming_requests.size})", user_requests_path(current_user), class: 'pure-menu-link' - %li.pure-menu-item - = link_to "Notifications (#{current_user.notifications.size})", user_notifications_path(current_user), class: 'pure-menu-link' + .menu + = link_to 'Tab', root_path, class: 'menu-heading menu-item' + .menu-list + = link_to 'Transactions', current_user, class: 'menu-item' + = link_to user_requests_path(current_user), class: 'menu-item' do + Requests + %span.badge= current_user.incoming_requests.open.size + = link_to user_notifications_path(current_user), class: 'menu-item' do + Notifications + %span.badge= current_user.notifications.unread.size + - if current_user.penning + = link_to 'Zeus', User.zeus, class: 'menu-item' + = link_to user_requests_path(User.zeus), class: 'menu-item' do + Zeus Requests + %span.badge= User.zeus.incoming_requests.size + = link_to user_notifications_path(User.zeus), class: 'menu-item' do + Zeus Notifications + %span.badge= User.zeus.notifications.size + .menu-list.menu-right + %span.menu-item= euro_from_cents current_user.balance + = link_to 'Sign out', sign_out_path, method: :delete, class: 'menu-item menu-item-signout' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 3be4dee..2c9e782 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -10,6 +10,6 @@ %body .pure-g = render 'menu' if current_user - .pure-u-1 + #content.pure-u-1 = render 'flash' = yield diff --git a/app/views/pages/_notifications.html.haml b/app/views/pages/_notifications.html.haml new file mode 100644 index 0000000..a3943a6 --- /dev/null +++ b/app/views/pages/_notifications.html.haml @@ -0,0 +1,16 @@ +.card-wrapper + - if @notifications.any? + .card + .padded + %h3 Notifications + - @notifications.each do |n| + .notification.pure-g + .pure-u-11-12 + = n.message + .pure-u-1-12.actions + = link_to notification_read_path(n), method: :post do + %span.glyphicon.glyphicon-eye-open + - else + .card.padded + %span.info-message + You have no unread notifications. diff --git a/app/views/pages/_outgoing_requests.html.haml b/app/views/pages/_outgoing_requests.html.haml new file mode 100644 index 0000000..83b85ff --- /dev/null +++ b/app/views/pages/_outgoing_requests.html.haml @@ -0,0 +1,17 @@ +.card-wrapper + - if @outgoing_requests.any? + .card + .padded + %h3 Outgoing Requests + - @outgoing_requests.each do |r| + .request.pure-g + .pure-u-2-3 + %h4= r.message + = r.debtor.name + .pure-u-1-3.actions + = euro_from_cents r.amount + .clear-both + - else + .card.padded + %span.info-message + You have no open outgoing requests at the moment. diff --git a/app/views/pages/_requests.html.haml b/app/views/pages/_requests.html.haml new file mode 100644 index 0000000..87f61b9 --- /dev/null +++ b/app/views/pages/_requests.html.haml @@ -0,0 +1,23 @@ +.card-wrapper + - if @requests.any? + .card + .padded + %h3 Requests + - @requests.each do |r| + .request.pure-g + .pure-u-1-3 + %h4= r.message + = r.creditor.name + .pure-u-1-3 + = euro_from_cents r.amount + .pure-u-1-3.actions + .btn-group + = link_to request_confirm_path(r), method: :post, class: 'btn btn-default btn-success' do + %span.glyphicon.glyphicon-ok + = link_to request_decline_path(r), method: :post, class: 'btn btn-default btn-danger' do + %span.glyphicon.glyphicon-remove + .clear-both + - else + .card.padded + %span.info-message + You have no open requests at the moment. diff --git a/app/views/pages/_transaction_form.html.haml b/app/views/pages/_transaction_form.html.haml new file mode 100644 index 0000000..2da34ad --- /dev/null +++ b/app/views/pages/_transaction_form.html.haml @@ -0,0 +1,3 @@ +.card-wrapper + .card.padded + = react_component 'TransactionForm', user: current_user, peers: User.all.order(:name).pluck(:name) diff --git a/app/views/pages/_transactions.html.haml b/app/views/pages/_transactions.html.haml new file mode 100644 index 0000000..53581c1 --- /dev/null +++ b/app/views/pages/_transactions.html.haml @@ -0,0 +1,21 @@ +.card-wrapper + .card + - @transactions.each do |t| + - t.symbolize_keys! + - date = Date.parse t[:time] + .transaction + .transaction-calendar + %span.transaction-day= date.strftime('%d') + %span.transaction-month= Date::MONTHNAMES[date.month][0..2] + .transaction-block + .transaction-block-l + %h4.transaction-message + = t[:message] + .transaction-peer + = t[:peer] + - if t[:peer] != t[:issuer] + .transaction-issuer + = "issued by #{t[:issuer]}" + .transaction-block-r + = euro_from_cents t[:amount] + .clear-both diff --git a/app/views/pages/landing.html.haml b/app/views/pages/landing.html.haml index 37d0477..87db746 100644 --- a/app/views/pages/landing.html.haml +++ b/app/views/pages/landing.html.haml @@ -1,43 +1,8 @@ -%h1.columns-title Tab -= javascript_include_tag "//www.google.com/jsapi", "chartkick" -- unless user_signed_in? - .pure-g.landing_columns - .pure-u-1.pure-u-md-1-2 - %h2 Authentication - %p Log een keer in en betaal uw schulden! - %p= link_to "Log in met Zeus WPI", user_omniauth_authorize_path(:zeuswpi), class: "pure-button pure-button-primary login-button" - .pure-u-1.pure-u-md-1-2 - %h2 Pie of Shame - = pie_chart @statistics.shamehash -- else - %h2.columns-title Cute Little Statistics - .pure-g - .pure-u-1.pure-u-md-1-2.landing-column - %h3.columns-title Pie of Shame - = pie_chart @statistics.shamehash - %h3.columns-title Table of Shame - %table.pure-table.full-table - %thead - %th Shame on - %th Contribution to Zeus' lack of money - %tbody - - @statistics.shameful_users.each do |user| - %tr - %td.shameful-person= user.name - // Won't divide by zero because there won't be any users with - // a shameful debt if the total debt is zero. - %td.shame-percentage= "#{-100 * user.balance / @statistics.total_debt}%" - .pure-u-1.pure-u-md-1-2.landing-column - %h3.columns-title Distribution of Debt Sources - = pie_chart @statistics.by_issuer - %h3.columns-title Top Debt Creators - %table.pure-table.full-table - %thead - %th Issuer - %th Number of Transactions issued - %tbody - - @statistics.creation_counts.each do |name, count| - %tr - %td.shameful-person= name - %td.shame-percentage= count - +.pure-g + .pure-u-7-12 + = render 'transactions' + .pure-u-5-12 + = render 'transaction_form' + = render 'requests' + = render 'notifications' + = render 'outgoing_requests' diff --git a/app/views/pages/sign_in_page.html.haml b/app/views/pages/sign_in_page.html.haml new file mode 100644 index 0000000..2795a68 --- /dev/null +++ b/app/views/pages/sign_in_page.html.haml @@ -0,0 +1,10 @@ +%h1.columns-title Tab += javascript_include_tag "//www.google.com/jsapi", "chartkick" +.pure-g.landing_columns + .pure-u-1.pure-u-md-1-2 + %h2 Authentication + %p Log een keer in en betaal uw schulden! + %p= link_to "Log in met Zeus WPI", user_omniauth_authorize_path(:zeuswpi), class: "pure-button pure-button-primary login-button" + .pure-u-1.pure-u-md-1-2 + %h2 Pie of Shame + = pie_chart @statistics.shamehash diff --git a/config/routes.rb b/config/routes.rb index bf9a17a..bf1b9d5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,7 +3,15 @@ Rails.application.routes.draw do omniauth_callbacks: 'callbacks' } - root to: 'pages#landing' + devise_scope :user do + delete '/sign_out', to: 'devise/sessions#destroy' + end + + authenticated :user do + root 'pages#landing', as: :authenticated_root + end + + root to: 'pages#sign_in_page' resources :transactions, only: [:index, :create] resources :users, only: [:index, :show] do