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