diff --git a/.esignore b/.esignore new file mode 100644 index 000000000..8430f8bb3 --- /dev/null +++ b/.esignore @@ -0,0 +1 @@ +app/assets/javascripts/bootstrap-accessibility.min.js diff --git a/.hound.yml b/.hound.yml index d62d2897a..6fc0f560d 100644 --- a/.hound.yml +++ b/.hound.yml @@ -8,3 +8,6 @@ scss: config_file: .scss-lint.yml eslint: config_file: .eslintrc + ignore_file: .esignore +jshint: + enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 1adfe8921..29056f7be 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-offense-counts` -# on 2018-01-11 09:25:35 +1300 using RuboCop version 0.49.1. +# on 2018-02-05 14:37:22 +1300 using RuboCop version 0.49.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -44,7 +44,6 @@ Rails/SkipsModelValidations: # SupportedStyles: strict, flexible Rails/TimeZone: Exclude: - - 'spec/controllers/accounts_controller_spec.rb' - 'spec/factories/member.rb' - 'spec/factories/post.rb' - 'spec/models/post_spec.rb' @@ -68,24 +67,11 @@ Style/IdenticalConditionalBranches: Exclude: - 'app/controllers/follows_controller.rb' -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: line_count_dependent, lambda, literal -Style/Lambda: - Exclude: - - 'spec/controllers/member_controller_spec.rb' - - 'spec/models/photo_spec.rb' - # Cop supports --auto-correct. Style/MultilineIfModifier: Exclude: - 'spec/rails_helper.rb' -# Cop supports --auto-correct. -Style/MutableConstant: - Exclude: - - 'app/models/planting.rb' - # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles. # SupportedStyles: predicate, comparison @@ -96,11 +82,6 @@ Style/NumericPredicate: - 'app/helpers/plantings_helper.rb' - 'lib/tasks/growstuff.rake' -# Cop supports --auto-correct. -Style/ParallelAssignment: - Exclude: - - 'app/mailers/notifier.rb' - # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes. # SupportedStyles: slashes, percent_r, mixed diff --git a/.travis.yml b/.travis.yml index 5d94d6a86..65db1b3f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,6 @@ script: else bundle exec rake db:migrate --trace; bundle exec rspec --tag $RSPEC_TAG spec/; - bundle exec rake jasmine:ci; fi; - set +e before_deploy: diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f984400ae..568137391 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -82,6 +82,7 @@ submit the change with your pull request. - Harry Brodsky / [hbrodsk1](https://github.com/hbrodsk1) - Jeff Kingswood / [ancyentmariner](https://github.com/ancyentmariner) - Logan Gingerich / [logangingerich](https://github.com/logangingerich) +- Mark Taffman / [mftaff](https://github.com/mftaff) ## Bots diff --git a/Gemfile b/Gemfile index 85b77b245..a6cb43608 100644 --- a/Gemfile +++ b/Gemfile @@ -38,7 +38,7 @@ gem 'gibbon', '~>1.2.0' # for Mailchimp newsletter subscriptions gem 'leaflet-rails' gem 'rails-assets-leaflet.markercluster', source: 'https://rails-assets.org' -gem 'pg' +gem 'pg', '< 1.0.0' # Upstream bug, see https://github.com/Growstuff/growstuff/pull/1539 gem 'ruby-units' # for unit conversion gem 'unicorn' # http server @@ -48,7 +48,6 @@ gem 'bootstrap-kaminari-views' # bootstrap views for kaminari gem 'kaminari' # pagination gem 'active_utils' -gem 'activemerchant' gem 'sidekiq' # Markdown formatting for updates etc @@ -123,7 +122,6 @@ group :development do end group :development, :test do - gem "active_merchant-paypal-bogus-gateway" gem 'bullet' # performance tuning by finding unnecesary queries gem 'byebug' # debugging gem 'capybara' # integration tests @@ -137,7 +135,6 @@ group :development, :test do gem 'haml-rails' # HTML templating language gem 'haml_lint' # Checks haml files for goodness gem 'i18n-tasks' # adds tests for finding missing and unused translations - gem 'jasmine' # javascript unit testing gem 'poltergeist' # for headless JS testing gem 'rainbow', '< 2.2.0' # See https://github.com/sickill/rainbow/issues/44 gem 'rspec-activemodel-mocks' @@ -156,3 +153,5 @@ end group :travis do gem 'platform-api' end +gem 'loofah', '>= 2.2.1' +gem 'rack-protection', '>= 2.0.1' diff --git a/Gemfile.lock b/Gemfile.lock index b7f899e78..cc5af9002 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -27,19 +27,12 @@ GEM rails-html-sanitizer (~> 1.0, >= 1.0.3) active_median (0.1.4) activerecord - active_merchant-paypal-bogus-gateway (0.1.0) - activemerchant - active_utils (3.3.9) - activesupport (>= 3.2, < 5.2.0) + active_utils (3.3.11) + activesupport (>= 3.2, < 6.0) i18n activejob (5.1.4) activesupport (= 5.1.4) globalid (>= 0.3.6) - activemerchant (1.76.0) - activesupport (>= 3.2.14, < 6.x) - builder (>= 2.1.2, < 4.0.0) - i18n (>= 0.6.9) - nokogiri (~> 1.4) activemodel (5.1.4) activesupport (= 5.1.4) activerecord (5.1.4) @@ -54,8 +47,8 @@ GEM addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) - ast (2.3.0) - autoprefixer-rails (7.2.4) + ast (2.4.0) + autoprefixer-rails (8.2.0) execjs bcrypt (3.1.11) better_errors (2.4.0) @@ -64,7 +57,7 @@ GEM rack (>= 0.9.0) bluecloth (2.2.0) bonsai-elasticsearch-rails (0.0.4) - bootstrap-datepicker-rails (1.7.1.1) + bootstrap-datepicker-rails (1.8.0.1) railties (>= 3.0) bootstrap-kaminari-views (0.0.5) kaminari (>= 0.13) @@ -73,12 +66,12 @@ GEM autoprefixer-rails (>= 5.2.1) sass (>= 3.3.4) builder (3.2.3) - bullet (5.7.0) + bullet (5.7.5) activesupport (>= 3.0.0) - uniform_notifier (~> 1.10.0) - byebug (9.1.0) - cancancan (2.1.2) - capybara (2.17.0) + uniform_notifier (~> 1.11.0) + byebug (10.0.2) + cancancan (2.1.3) + capybara (2.18.0) addressable mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) @@ -91,8 +84,8 @@ GEM capybara-screenshot (1.0.18) capybara (>= 1.0, < 3) launchy - chartkick (2.2.5) - childprocess (0.8.0) + chartkick (2.3.3) + childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) cliver (0.3.2) codeclimate-test-reporter (1.0.8) @@ -116,18 +109,17 @@ GEM crass (1.0.3) csv_shaper (1.3.0) activesupport (>= 3.0.0) - dalli (2.7.6) + dalli (2.7.7) database_cleaner (1.6.2) - devise (4.4.0) + devise (4.4.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 4.1.0, < 5.2) + railties (>= 4.1.0, < 6.0) responders warden (~> 1.2.3) diff-lcs (1.3) docile (1.1.5) - easy_translate (0.5.0) - json + easy_translate (0.5.1) thread thread_safe elasticsearch (5.0.4) @@ -143,9 +135,9 @@ GEM elasticsearch-transport (5.0.4) faraday multi_json - erubi (1.7.0) + erubi (1.7.1) erubis (2.7.0) - excon (0.60.0) + excon (0.62.0) execjs (2.7.0) factory_bot (4.8.2) activesupport (>= 3.0.0) @@ -156,15 +148,15 @@ GEM i18n (>= 0.7) faraday (0.12.2) multipart-post (>= 1.2, < 3) - ffi (1.9.18) + ffi (1.9.23) figaro (1.1.1) thor (~> 0.14) flickraw (0.9.9) - font-awesome-sass (4.7.0) + font-awesome-sass (5.0.9) sass (>= 3.2) friendly_id (5.2.3) activerecord (>= 4.0.0) - geocoder (1.4.5) + geocoder (1.4.7) gibbon (1.2.1) httparty multi_json (>= 1.9.0) @@ -206,9 +198,9 @@ GEM haml (>= 4.0, < 6) nokogiri (>= 1.6.0) ruby_parser (~> 3.5) - httparty (0.15.6) + httparty (0.16.2) multi_xml (>= 0.5.2) - i18n (0.9.1) + i18n (0.9.5) concurrent-ruby (~> 1.0) i18n-tasks (0.9.12) activesupport (>= 4.0.2) @@ -220,12 +212,6 @@ GEM parser (>= 2.2.3.0) term-ansicolor (>= 1.3.2) terminal-table (>= 1.5.1) - jasmine (2.8.0) - jasmine-core (>= 2.8.0, < 3.0.0) - phantomjs - rack (>= 1.2.1) - rake - jasmine-core (2.8.0) jquery-rails (4.3.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) @@ -253,18 +239,18 @@ GEM activerecord kaminari-core (= 1.1.1) kaminari-core (1.1.1) - kgio (2.11.1) + kgio (2.11.2) launchy (2.4.3) addressable (~> 2.3) - leaflet-rails (1.2.0) + leaflet-rails (1.3.1) rails (>= 4.2.0) - letter_opener (1.5.0) + letter_opener (1.6.0) launchy (~> 2.2) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) - loofah (2.1.1) + loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.0) @@ -273,14 +259,14 @@ GEM method_source (0.9.0) mini_mime (1.0.0) mini_portile2 (2.3.0) - minitest (5.11.1) + minitest (5.11.3) moneta (0.8.1) multi_json (1.11.3) multi_xml (0.6.0) multipart-post (2.0.0) - newrelic_rpm (4.7.1.340) - nio4r (2.2.0) - nokogiri (1.8.1) + newrelic_rpm (5.0.0.342) + nio4r (2.3.0) + nokogiri (1.8.2) mini_portile2 (~> 2.3.0) oauth (0.5.4) oauth2 (1.4.0) @@ -310,10 +296,9 @@ GEM parallel (1.12.1) paranoia (2.4.0) activerecord (>= 4.0, < 5.2) - parser (2.4.0.2) - ast (~> 2.3) + parser (2.5.0.5) + ast (~> 2.4.0) pg (0.21.0) - phantomjs (2.1.1.0) platform-api (2.1.0) heroics (~> 0.0.23) moneta (~> 0.8.1) @@ -322,11 +307,11 @@ GEM cliver (~> 0.3.1) websocket-driver (>= 0.2.0) powerpack (0.1.1) - public_suffix (3.0.1) - rack (2.0.3) - rack-protection (2.0.0) + public_suffix (3.0.2) + rack (2.0.4) + rack-protection (2.0.1) rack - rack-test (0.8.2) + rack-test (1.0.0) rack (>= 1.0, < 3) rails (5.1.4) actioncable (= 5.1.4) @@ -340,8 +325,8 @@ GEM bundler (>= 1.3.0) railties (= 5.1.4) sprockets-rails (>= 2.0.0) - rails-assets-leaflet (1.2.0) - rails-assets-leaflet.markercluster (1.2.0) + rails-assets-leaflet (1.3.1) + rails-assets-leaflet.markercluster (1.3.0) rails-assets-leaflet (>= 1.0.3) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) @@ -350,8 +335,8 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) rails_12factor (0.0.3) rails_serve_static_assets rails_stdout_logging @@ -365,8 +350,8 @@ GEM thor (>= 0.18.1, < 2.0) rainbow (2.1.0) raindrops (0.19.0) - rake (12.3.0) - rb-fsevent (0.10.2) + rake (12.3.1) + rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) redis (4.0.1) @@ -393,7 +378,7 @@ GEM rspec-expectations (~> 3.7.0) rspec-mocks (~> 3.7.0) rspec-support (~> 3.7.0) - rspec-support (3.7.0) + rspec-support (3.7.1) rubocop (0.49.1) parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) @@ -402,12 +387,12 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.9.0) - ruby-units (2.2.1) + ruby-units (2.3.0) ruby_dep (1.5.0) - ruby_parser (3.10.1) + ruby_parser (3.11.0) sexp_processor (~> 4.9) rubyzip (1.2.1) - sass (3.5.4) + sass (3.5.6) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) @@ -418,15 +403,15 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - selenium-webdriver (3.8.0) + selenium-webdriver (3.11.0) childprocess (~> 0.5) - rubyzip (~> 1.0) - sexp_processor (4.10.0) - sidekiq (5.0.5) + rubyzip (~> 1.2) + sexp_processor (4.10.1) + sidekiq (5.1.2) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) - redis (>= 3.3.4, < 5) + redis (>= 3.3.5, < 5) simplecov (0.12.0) docile (~> 1.1.0) json (>= 1.8, < 3) @@ -454,15 +439,15 @@ GEM timecop (0.9.1) tins (1.16.3) trollop (1.16.2) - tzinfo (1.2.4) + tzinfo (1.2.5) thread_safe (~> 0.1) - uglifier (4.1.2) + uglifier (4.1.8) execjs (>= 0.3.0, < 3) unicode-display_width (1.3.0) unicorn (5.4.0) kgio (~> 2.6) raindrops (~> 0.7) - uniform_notifier (1.10.0) + uniform_notifier (1.11.0) warden (1.2.7) rack (>= 1.0) webrat (0.7.3) @@ -482,9 +467,7 @@ PLATFORMS DEPENDENCIES active_median - active_merchant-paypal-bogus-gateway active_utils - activemerchant better_errors bluecloth bonsai-elasticsearch-rails @@ -524,7 +507,6 @@ DEPENDENCIES haml_lint hashie (>= 3.5.3) i18n-tasks - jasmine jquery-rails jquery-ui-rails js-routes @@ -533,6 +515,7 @@ DEPENDENCIES leaflet-rails letter_opener listen + loofah (>= 2.2.1) memcachier newrelic_rpm omniauth (~> 1.3) @@ -540,9 +523,10 @@ DEPENDENCIES omniauth-flickr (>= 0.0.15) omniauth-twitter paranoia (~> 2.2) - pg + pg (< 1.0.0) platform-api poltergeist + rack-protection (>= 2.0.1) rails (= 5.1.4) rails-assets-leaflet.markercluster! rails-controller-testing diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 30c74ce72..419c55d00 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -10,8 +10,6 @@ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD // GO AFTER THE REQUIRES BELOW. // -// = require Chart.bundle -// = require chartkick // = require leaflet // = require leaflet.markercluster // = require js-routes diff --git a/app/assets/javascripts/bootstrap-accessibility.min.js b/app/assets/javascripts/bootstrap-accessibility.min.js index 8e5a2ef61..41a37c453 100644 --- a/app/assets/javascripts/bootstrap-accessibility.min.js +++ b/app/assets/javascripts/bootstrap-accessibility.min.js @@ -1,4 +1,4 @@ -/*! bootstrap-accessibility-plugin - v1.0.4 - 2015-07-27 +/*! bootstrap-accessibility-plugin - v1.0.5 - 2016-07-19 * https://github.com/paypal/bootstrap-accessibility-plugin -* Copyright (c) 2015 PayPal Accessibility Team; Licensed BSD */ -!function($){"use strict";var uniqueId=function(prefix){return(prefix||"ui-id")+"-"+Math.floor(1e3*Math.random()+1)},focusable=function(element,isTabIndexNotNaN){var map,mapName,img,nodeName=element.nodeName.toLowerCase();return"area"===nodeName?(map=element.parentNode,mapName=map.name,element.href&&mapName&&"map"===map.nodeName.toLowerCase()?(img=$("img[usemap='#"+mapName+"']")[0],!!img&&visible(img)):!1):(/input|select|textarea|button|object/.test(nodeName)?!element.disabled:"a"===nodeName?element.href||isTabIndexNotNaN:isTabIndexNotNaN)&&visible(element)},visible=function(element){return $.expr.filters.visible(element)&&!$(element).parents().addBack().filter(function(){return"hidden"===$.css(this,"visibility")}).length};$.extend($.expr[":"],{data:$.expr.createPseudo?$.expr.createPseudo(function(dataName){return function(elem){return!!$.data(elem,dataName)}}):function(elem,i,match){return!!$.data(elem,match[3])},focusable:function(element){return focusable(element,!isNaN($.attr(element,"tabindex")))},tabbable:function(element){var tabIndex=$.attr(element,"tabindex"),isTabIndexNaN=isNaN(tabIndex);return(isTabIndexNaN||tabIndex>=0)&&focusable(element,!isTabIndexNaN)}}),$(".modal-dialog").attr({role:"document"});var modalhide=$.fn.modal.Constructor.prototype.hide;$.fn.modal.Constructor.prototype.hide=function(){var modalOpener=this.$element.parent().find('[data-target="#'+this.$element.attr("id")+'"]');modalhide.apply(this,arguments),modalOpener.focus(),$(document).off("keydown.bs.modal")};var modalfocus=$.fn.modal.Constructor.prototype.enforceFocus;$.fn.modal.Constructor.prototype.enforceFocus=function(){var focEls=this.$element.find(":tabbable"),lastEl=focEls[focEls.length-1];$(document).on("keydown.bs.modal",$.proxy(function(ev){!this.$element.has(ev.target).length&&ev.shiftKey&&9===ev.keyCode&&(lastEl.focus(),ev.preventDefault())},this)),modalfocus.apply(this,arguments)};var $par,firstItem,toggle="[data-toggle=dropdown]",focusDelay=200,menus=$(toggle).parent().find("ul").attr("role","menu"),lis=menus.find("li").attr("role","presentation");lis.find("a").attr({role:"menuitem",tabIndex:"-1"}),$(toggle).attr({"aria-haspopup":"true","aria-expanded":"false"}),$(toggle).parent().on("shown.bs.dropdown",function(e){$par=$(this);var $toggle=$par.find(toggle);$toggle.attr("aria-expanded","true"),$toggle.on("keydown.bs.dropdown",$.proxy(function(ev){setTimeout(function(){firstItem=$(".dropdown-menu [role=menuitem]:visible",$par)[0];try{firstItem.focus()}catch(ex){}},focusDelay)},this))}),$(toggle).parent().on("hidden.bs.dropdown",function(e){$par=$(this);var $toggle=$par.find(toggle);$toggle.attr("aria-expanded","false")}),$(document).on("focusout.dropdown.data-api",".dropdown-menu",function(e){var $this=$(this),that=this;setTimeout(function(){$.contains(that,document.activeElement)||($this.parent().removeClass("open"),$this.parent().find("[data-toggle=dropdown]").attr("aria-expanded","false"))},150)}).on("keydown.bs.dropdown.data-api",toggle+", [role=menu]",$.fn.dropdown.Constructor.prototype.keydown);var $tablist=$(".nav-tabs, .nav-pills"),$lis=$tablist.children("li"),$tabs=$tablist.find('[data-toggle="tab"], [data-toggle="pill"]');$tabs&&($tablist.attr("role","tablist"),$lis.attr("role","presentation"),$tabs.attr("role","tab")),$tabs.each(function(index){var tabpanel=$($(this).attr("href")),tab=$(this),tabid=tab.attr("id")||uniqueId("ui-tab");tab.attr("id",tabid),tab.parent().hasClass("active")?(tab.attr({tabIndex:"0","aria-selected":"true","aria-controls":tab.attr("href").substr(1)}),tabpanel.attr({role:"tabpanel",tabIndex:"0","aria-hidden":"false","aria-labelledby":tabid})):(tab.attr({tabIndex:"-1","aria-selected":"false","aria-controls":tab.attr("href").substr(1)}),tabpanel.attr({role:"tabpanel",tabIndex:"-1","aria-hidden":"true","aria-labelledby":tabid}))}),$.fn.tab.Constructor.prototype.keydown=function(e){var $items,index,$this=$(this),$ul=$this.closest("ul[role=tablist] "),k=e.which||e.keyCode;if($this=$(this),/(37|38|39|40)/.test(k)){$items=$ul.find("[role=tab]:visible"),index=$items.index($items.filter(":focus")),(38==k||37==k)&&index--,(39==k||40==k)&&index++,0>index&&(index=$items.length-1),index==$items.length&&(index=0);var nextTab=$items.eq(index);"tab"===nextTab.attr("role")&&nextTab.tab("show").focus(),e.preventDefault(),e.stopPropagation()}},$(document).on("keydown.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',$.fn.tab.Constructor.prototype.keydown);var tabactivate=$.fn.tab.Constructor.prototype.activate;$.fn.tab.Constructor.prototype.activate=function(element,container,callback){var $active=container.find("> .active");$active.find("[data-toggle=tab], [data-toggle=pill]").attr({tabIndex:"-1","aria-selected":!1}),$active.filter(".tab-pane").attr({"aria-hidden":!0,tabIndex:"-1"}),tabactivate.apply(this,arguments),element.addClass("active"),element.find("[data-toggle=tab], [data-toggle=pill]").attr({tabIndex:"0","aria-selected":!0}),element.filter(".tab-pane").attr({"aria-hidden":!1,tabIndex:"0"})};var $colltabs=$('[data-toggle="collapse"]');$colltabs.each(function(index){var colltab=$(this),collpanel=$(colltab.attr("data-target")?colltab.attr("data-target"):colltab.attr("href")),parent=colltab.attr("data-parent"),collparent=parent&&$(parent),collid=colltab.attr("id")||uniqueId("ui-collapse");colltab.attr("id",collid),collparent&&(colltab.attr({role:"tab","aria-selected":"false","aria-expanded":"false"}),$(collparent).find("div:not(.collapse,.panel-body), h4").attr("role","presentation"),collparent.attr({role:"tablist","aria-multiselectable":"true"}),collpanel.hasClass("in")?(colltab.attr({"aria-controls":collpanel.attr("id"),"aria-selected":"true","aria-expanded":"true",tabindex:"0"}),collpanel.attr({role:"tabpanel",tabindex:"0","aria-labelledby":collid,"aria-hidden":"false"})):(colltab.attr({"aria-controls":collpanel.attr("id"),tabindex:"-1"}),collpanel.attr({role:"tabpanel",tabindex:"-1","aria-labelledby":collid,"aria-hidden":"true"})))});var collToggle=$.fn.collapse.Constructor.prototype.toggle;$.fn.collapse.Constructor.prototype.toggle=function(){var href,prevTab=this.$parent&&this.$parent.find('[aria-expanded="true"]');if(prevTab){var curTab,prevPanel=prevTab.attr("data-target")||(href=prevTab.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,""),$prevPanel=$(prevPanel),$curPanel=this.$element;this.$parent;this.$parent&&(curTab=this.$parent.find('[data-toggle=collapse][href="#'+this.$element.attr("id")+'"]')),collToggle.apply(this,arguments),$.support.transition&&this.$element.one($.support.transition.end,function(){prevTab.attr({"aria-selected":"false","aria-expanded":"false",tabIndex:"-1"}),$prevPanel.attr({"aria-hidden":"true",tabIndex:"-1"}),curTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:"0"}),$curPanel.hasClass("in")?$curPanel.attr({"aria-hidden":"false",tabIndex:"0"}):(curTab.attr({"aria-selected":"false","aria-expanded":"false"}),$curPanel.attr({"aria-hidden":"true",tabIndex:"-1"}))})}else collToggle.apply(this,arguments)},$.fn.collapse.Constructor.prototype.keydown=function(e){var $items,index,$this=$(this),$tablist=$this.closest("div[role=tablist] "),k=e.which||e.keyCode;$this=$(this),/(32|37|38|39|40)/.test(k)&&(32==k&&$this.click(),$items=$tablist.find("[role=tab]"),index=$items.index($items.filter(":focus")),(38==k||37==k)&&index--,(39==k||40==k)&&index++,0>index&&(index=$items.length-1),index==$items.length&&(index=0),$items.eq(index).focus(),e.preventDefault(),e.stopPropagation())},$(document).on("keydown.collapse.data-api",'[data-toggle="collapse"]',$.fn.collapse.Constructor.prototype.keydown),$(".carousel").each(function(index){var $this=$(this),prev=$this.find('[data-slide="prev"]'),next=$this.find('[data-slide="next"]'),$options=$this.find(".item"),$listbox=$options.parent();$this.attr({"data-interval":"false","data-wrap":"false"}),$listbox.attr("role","listbox"),$options.attr("role","option");var spanPrev=document.createElement("span");spanPrev.setAttribute("class","sr-only"),spanPrev.innerHTML="Previous";var spanNext=document.createElement("span");spanNext.setAttribute("class","sr-only"),spanNext.innerHTML="Next",prev.attr("role","button"),next.attr("role","button"),prev.append(spanPrev),next.append(spanNext),$options.each(function(){var item=$(this);item.hasClass("active")?item.attr({"aria-selected":"true",tabindex:"0"}):item.attr({"aria-selected":"false",tabindex:"-1"})})});var slideCarousel=$.fn.carousel.Constructor.prototype.slide;$.fn.carousel.Constructor.prototype.slide=function(type,next){var $active=this.$element.find(".item.active"),$next=next||$active[type]();slideCarousel.apply(this,arguments),$active.one("bsTransitionEnd",function(){$active.attr({"aria-selected":!1,tabIndex:"-1"}),$next.attr({"aria-selected":!0,tabIndex:"0"})})};var $this;$.fn.carousel.Constructor.prototype.keydown=function(e){$this=$this||$(this),this instanceof Node&&($this=$(this));var index,$ul=$this.closest("div[role=listbox]"),$items=$ul.find("[role=option]"),$parent=$ul.parent(),k=e.which||e.keyCode;/(37|38|39|40)/.test(k)&&(index=$items.index($items.filter(".active")),(37==k||38==k)&&(index--,0>index?index=$items.length-1:($parent.carousel("prev"),setTimeout(function(){$items[index].focus()},150))),(39==k||40==k)&&(index++,index==$items.length?index=0:($parent.carousel("next"),setTimeout(function(){$items[index].focus()},150))),e.preventDefault(),e.stopPropagation())},$(document).on("keydown.carousel.data-api","div[role=option]",$.fn.carousel.Constructor.prototype.keydown)}(jQuery); +* Copyright (c) 2016 PayPal Accessibility Team; Licensed BSD */ +!function($){"use strict";var uniqueId=function(prefix){return(prefix||"ui-id")+"-"+Math.floor(1e3*Math.random()+1)},focusable=function(element,isTabIndexNotNaN){var map,mapName,img,nodeName=element.nodeName.toLowerCase();return"area"===nodeName?(map=element.parentNode,mapName=map.name,element.href&&mapName&&"map"===map.nodeName.toLowerCase()?(img=$("img[usemap='#"+mapName+"']")[0],!!img&&visible(img)):!1):(/input|select|textarea|button|object/.test(nodeName)?!element.disabled:"a"===nodeName?element.href||isTabIndexNotNaN:isTabIndexNotNaN)&&visible(element)},visible=function(element){return $.expr.filters.visible(element)&&!$(element).parents().addBack().filter(function(){return"hidden"===$.css(this,"visibility")}).length};$.extend($.expr[":"],{data:$.expr.createPseudo?$.expr.createPseudo(function(dataName){return function(elem){return!!$.data(elem,dataName)}}):function(elem,i,match){return!!$.data(elem,match[3])},focusable:function(element){return focusable(element,!isNaN($.attr(element,"tabindex")))},tabbable:function(element){var tabIndex=$.attr(element,"tabindex"),isTabIndexNaN=isNaN(tabIndex);return(isTabIndexNaN||tabIndex>=0)&&focusable(element,!isTabIndexNaN)}}),$(".modal-dialog").attr({role:"document"});var modalhide=$.fn.modal.Constructor.prototype.hide;$.fn.modal.Constructor.prototype.hide=function(){modalhide.apply(this,arguments),$(document).off("keydown.bs.modal")};var modalfocus=$.fn.modal.Constructor.prototype.enforceFocus;$.fn.modal.Constructor.prototype.enforceFocus=function(){var $content=this.$element.find(".modal-content"),focEls=$content.find(":tabbable"),$lastEl=$(focEls[focEls.length-1]),$firstEl=$(focEls[0]);$lastEl.on("keydown.bs.modal",$.proxy(function(ev){9!==ev.keyCode||ev.shiftKey|ev.ctrlKey|ev.metaKey|ev.altKey||(ev.preventDefault(),$firstEl.focus())},this)),$firstEl.on("keydown.bs.modal",$.proxy(function(ev){9===ev.keyCode&&ev.shiftKey&&(ev.preventDefault(),$lastEl.focus())},this)),modalfocus.apply(this,arguments)};var $par,firstItem,toggle="[data-toggle=dropdown]",focusDelay=200,menus=$(toggle).parent().find("ul").attr("role","menu"),lis=menus.find("li").attr("role","presentation");lis.find("a").attr({role:"menuitem",tabIndex:"-1"}),$(toggle).attr({"aria-haspopup":"true","aria-expanded":"false"}),$(toggle).parent().on("shown.bs.dropdown",function(e){$par=$(this);var $toggle=$par.find(toggle);$toggle.attr("aria-expanded","true"),$toggle.on("keydown.bs.dropdown",$.proxy(function(ev){setTimeout(function(){firstItem=$(".dropdown-menu [role=menuitem]:visible",$par)[0];try{firstItem.focus()}catch(ex){}},focusDelay)},this))}).on("hidden.bs.dropdown",function(e){$par=$(this);var $toggle=$par.find(toggle);$toggle.attr("aria-expanded","false")}),$(document).on("focusout.dropdown.data-api",".dropdown-menu",function(e){var $this=$(this),that=this;$this.parent().hasClass("open")&&setTimeout(function(){$.contains(that,document.activeElement)||$this.parent().find("[data-toggle=dropdown]").dropdown("toggle")},150)}).on("keydown.bs.dropdown.data-api",toggle+", [role=menu]",$.fn.dropdown.Constructor.prototype.keydown);var $tablist=$(".nav-tabs, .nav-pills"),$lis=$tablist.children("li"),$tabs=$tablist.find('[data-toggle="tab"], [data-toggle="pill"]');$tabs&&($tablist.attr("role","tablist"),$lis.attr("role","presentation"),$tabs.attr("role","tab")),$tabs.each(function(index){var tabpanel=$($(this).attr("href")),tab=$(this),tabid=tab.attr("id")||uniqueId("ui-tab");tab.attr("id",tabid),tab.parent().hasClass("active")?(tab.attr({tabIndex:"0","aria-selected":"true","aria-controls":tab.attr("href").substr(1)}),tabpanel.attr({role:"tabpanel",tabIndex:"0","aria-hidden":"false","aria-labelledby":tabid})):(tab.attr({tabIndex:"-1","aria-selected":"false","aria-controls":tab.attr("href").substr(1)}),tabpanel.attr({role:"tabpanel",tabIndex:"-1","aria-hidden":"true","aria-labelledby":tabid}))}),$.fn.tab.Constructor.prototype.keydown=function(e){var $items,index,$this=$(this),$ul=$this.closest("ul[role=tablist] "),k=e.which||e.keyCode;if($this=$(this),/(37|38|39|40)/.test(k)){$items=$ul.find("[role=tab]:visible"),index=$items.index($items.filter(":focus")),(38==k||37==k)&&index--,(39==k||40==k)&&index++,0>index&&(index=$items.length-1),index==$items.length&&(index=0);var nextTab=$items.eq(index);"tab"===nextTab.attr("role")&&nextTab.tab("show").focus(),e.preventDefault(),e.stopPropagation()}},$(document).on("keydown.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',$.fn.tab.Constructor.prototype.keydown);var tabactivate=$.fn.tab.Constructor.prototype.activate;$.fn.tab.Constructor.prototype.activate=function(element,container,callback){var $active=container.find("> .active");$active.find("[data-toggle=tab], [data-toggle=pill]").attr({tabIndex:"-1","aria-selected":!1}),$active.filter(".tab-pane").attr({"aria-hidden":!0,tabIndex:"-1"}),tabactivate.apply(this,arguments),element.addClass("active"),element.find("[data-toggle=tab], [data-toggle=pill]").attr({tabIndex:"0","aria-selected":!0}),element.filter(".tab-pane").attr({"aria-hidden":!1,tabIndex:"0"})};var $colltabs=$('[data-toggle="collapse"]');$colltabs.each(function(index){var colltab=$(this),collpanel=$(colltab.attr("data-target")?colltab.attr("data-target"):colltab.attr("href")),parent=colltab.attr("data-parent"),collparent=parent&&$(parent),collid=colltab.attr("id")||uniqueId("ui-collapse");colltab.attr("id",collid),collparent&&(colltab.attr({role:"tab","aria-selected":"false","aria-expanded":"false"}),$(collparent).find("div:not(.collapse,.panel-body), h4").attr("role","presentation"),collparent.attr({role:"tablist","aria-multiselectable":"true"}),collpanel.hasClass("in")?(colltab.attr({"aria-controls":collpanel.attr("id"),"aria-selected":"true","aria-expanded":"true",tabindex:"0"}),collpanel.attr({role:"tabpanel",tabindex:"0","aria-labelledby":collid,"aria-hidden":"false"})):(colltab.attr({"aria-controls":collpanel.attr("id"),tabindex:"-1"}),collpanel.attr({role:"tabpanel",tabindex:"-1","aria-labelledby":collid,"aria-hidden":"true"})))});var collToggle=$.fn.collapse.Constructor.prototype.toggle;$.fn.collapse.Constructor.prototype.toggle=function(){var href,prevTab=this.$parent&&this.$parent.find('[aria-expanded="true"]');if(prevTab){var curTab,prevPanel=prevTab.attr("data-target")||(href=prevTab.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,""),$prevPanel=$(prevPanel),$curPanel=this.$element;this.$parent;this.$parent&&(curTab=this.$parent.find('[data-toggle=collapse][href="#'+this.$element.attr("id")+'"]')),collToggle.apply(this,arguments),$.support.transition&&this.$element.one($.support.transition.end,function(){prevTab.attr({"aria-selected":"false","aria-expanded":"false",tabIndex:"-1"}),$prevPanel.attr({"aria-hidden":"true",tabIndex:"-1"}),curTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:"0"}),$curPanel.hasClass("in")?$curPanel.attr({"aria-hidden":"false",tabIndex:"0"}):(curTab.attr({"aria-selected":"false","aria-expanded":"false"}),$curPanel.attr({"aria-hidden":"true",tabIndex:"-1"}))})}else collToggle.apply(this,arguments)},$.fn.collapse.Constructor.prototype.keydown=function(e){var $items,index,$this=$(this),$tablist=$this.closest("div[role=tablist] "),k=e.which||e.keyCode;$this=$(this),/(32|37|38|39|40)/.test(k)&&(32==k&&$this.click(),$items=$tablist.find("[role=tab]"),index=$items.index($items.filter(":focus")),(38==k||37==k)&&index--,(39==k||40==k)&&index++,0>index&&(index=$items.length-1),index==$items.length&&(index=0),$items.eq(index).focus(),e.preventDefault(),e.stopPropagation())},$(document).on("keydown.collapse.data-api",'[data-toggle="collapse"]',$.fn.collapse.Constructor.prototype.keydown),$(".carousel").each(function(index){function setTablistHighlightBox(){var $tab,offset,height,width,highlightBox={};highlightBox.top=0,highlightBox.left=32e3,highlightBox.height=0,highlightBox.width=0;for(var i=0;i<$tabs.length;i++){$tab=$tabs[i],offset=$($tab).offset(),height=$($tab).height(),width=$($tab).width(),highlightBox.topoffset.left&&(highlightBox.left=Math.round(offset.left));var w=offset.left-highlightBox.left+Math.round(width);highlightBox.widthA carousel is a rotating set of images, rotation stops on keyboard focus on carousel tab controls or hovering the mouse pointer over images. Use the tabs or the previous and next buttons to change the displayed slide.

'),$this.prepend('

Carousel content with '+$tabpanels.length+" slides.

")),i=0;i<$tabs.length;i++){$tab=$tabs[i],$tab.setAttribute("role","tab"),$tab.setAttribute("id","tab-"+index+"-"+i),$tab.setAttribute("aria-controls","tabpanel-"+index+"-"+i);var tpId="#tabpanel-"+index+"-"+i,caption=$this.find(tpId).find("h1").text();("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h3").text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h4").text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h5").text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h6").text()),("string"!=typeof caption||0===caption.length)&&(caption="no title");var tabName=document.createElement("span");tabName.setAttribute("class","sr-only"),tabName.innerHTML="Slide "+(i+1),caption&&(tabName.innerHTML+=": "+caption),$tab.appendChild(tabName)}$tablistHighlight=document.createElement("div"),$tablistHighlight.className="carousel-tablist-highlight",document.body.appendChild($tablistHighlight),$complementaryLandmark=document.createElement("aside"),$complementaryLandmark.setAttribute("class","carousel-aside-pause"),$complementaryLandmark.setAttribute("aria-label","carousel pause/play control"),$this.prepend($complementaryLandmark),$pauseCarousel=document.createElement("button"),$pauseCarousel.className="carousel-pause-button",$pauseCarousel.innerHTML="Pause Carousel",$pauseCarousel.setAttribute("title","Pause/Play carousel button can be used by screen reader users to stop carousel animations"),$($complementaryLandmark).append($pauseCarousel),$($pauseCarousel).click(function(){$is_paused?($pauseCarousel.innerHTML="Pause Carousel",$this.carousel("cycle"),$is_paused=!1):($pauseCarousel.innerHTML="Play Carousel",$this.carousel("pause"),$is_paused=!0)}),$($pauseCarousel).focus(function(){$(this).addClass("focus")}),$($pauseCarousel).blur(function(){$(this).removeClass("focus")}),setTablistHighlightBox(),$(window).resize(function(){setTablistHighlightBox()}),$prev.attr("aria-label","Previous Slide"),$prev.keydown(function(e){var k=e.which||e.keyCode;/(13|32)/.test(k)&&(e.preventDefault(),e.stopPropagation(),$prev.trigger("click"))}),$prev.focus(function(){$(this).parents(".carousel").addClass("contrast")}),$prev.blur(function(){$(this).parents(".carousel").removeClass("contrast")}),$next.attr("aria-label","Next Slide"),$next.keydown(function(e){var k=e.which||e.keyCode;/(13|32)/.test(k)&&(e.preventDefault(),e.stopPropagation(),$next.trigger("click"))}),$next.focus(function(){$(this).parents(".carousel").addClass("contrast")}),$next.blur(function(){$(this).parents(".carousel").removeClass("contrast")}),$(".carousel-inner a").focus(function(){$(this).parents(".carousel").addClass("contrast")}),$(".carousel-inner a").blur(function(){$(this).parents(".carousel").removeClass("contrast")}),$tabs.each(function(){var item=$(this);item.hasClass("active")?item.attr({"aria-selected":"true",tabindex:"0"}):item.attr({"aria-selected":"false",tabindex:"-1"})})});var slideCarousel=$.fn.carousel.Constructor.prototype.slide;$.fn.carousel.Constructor.prototype.slide=function(type,next){var $id,$element=this.$element,$active=$element.find("[role=tabpanel].active"),$next=next||$active[type](),$tab_count=$element.find("[role=tabpanel]").size(),$prev_side=$element.find('[data-slide="prev"]'),$next_side=$element.find('[data-slide="next"]'),$index=0,$prev_index=$tab_count-1,$next_index=1;$next&&$next.attr("id")&&($id=$next.attr("id"),$index=$id.lastIndexOf("-"),$index>=0&&($index=parseInt($id.substring($index+1),10)),$prev_index=$index-1,1>$prev_index&&($prev_index=$tab_count-1),$next_index=$index+1,$next_index>=$tab_count&&($next_index=0)),$prev_side.attr("aria-label","Show slide "+($prev_index+1)+" of "+$tab_count),$next_side.attr("aria-label","Show slide "+($next_index+1)+" of "+$tab_count),slideCarousel.apply(this,arguments),$active.one("bsTransitionEnd",function(){var $tab;$tab=$element.find('li[aria-controls="'+$active.attr("id")+'"]'),$tab&&$tab.attr({"aria-selected":!1,tabIndex:"-1"}),$tab=$element.find('li[aria-controls="'+$next.attr("id")+'"]'),$tab&&$tab.attr({"aria-selected":!0,tabIndex:"0"})})};var $this;$.fn.carousel.Constructor.prototype.keydown=function(e){function selectTab(index){index>=$tabs.length||0>index||($carousel.carousel(index),setTimeout(function(){$tabs[index].focus()},150))}$this=$this||$(this),this instanceof Node&&($this=$(this));var index,$carousel=$(e.target).closest(".carousel"),$tabs=$carousel.find("[role=tab]"),k=e.which||e.keyCode;/(37|38|39|40)/.test(k)&&(index=$tabs.index($tabs.filter(".active")),(37==k||38==k)&&(index--,selectTab(index)),(39==k||40==k)&&(index++,selectTab(index)),e.preventDefault(),e.stopPropagation())},$(document).on("keydown.carousel.data-api","li[role=tab]",$.fn.carousel.Constructor.prototype.keydown)}(jQuery); \ No newline at end of file diff --git a/app/assets/javascripts/charts.js b/app/assets/javascripts/charts.js new file mode 100644 index 000000000..1c42da456 --- /dev/null +++ b/app/assets/javascripts/charts.js @@ -0,0 +1,2 @@ +// = require Chart.bundle +// = require chartkick diff --git a/app/assets/javascripts/crops.js.coffee b/app/assets/javascripts/crops.js.coffee new file mode 100644 index 000000000..6a0a27123 --- /dev/null +++ b/app/assets/javascripts/crops.js.coffee @@ -0,0 +1,39 @@ +jQuery -> + $('#add-sci_name-row').css("display", "inline-block") + $('#remove-sci_name-row').css("display", "inline-block") + $("#add-alt_name-row").css("display", "inline-block") + $("#remove-alt_name-row").css("display", "inline-block") + +-$ -> + sci_template = "
Scientific name of crop.
" + + sci_index = $('#scientific_names .template').length + 1 + + $('#add-sci_name-row').click -> + compiled_input = $(sci_template.split("INDEX").join(sci_index)) + $('#scientific_names').append(compiled_input) + sci_index = sci_index + 1 + + $('#remove-sci_name-row').click -> + if (sci_index > 2) + sci_index = sci_index - 1 + tmp = 'sci_template[' + sci_index + ']' + element = document.getElementById(tmp) + element.remove() + + alt_template = "
Alternate name of crop.
" + + alt_index = $('#alternate_names .template').length + 1 + + $('#add-alt_name-row').click -> + compiled_input = $(alt_template.split("INDEX").join(alt_index)) + $('#alternate_names').append(compiled_input) + alt_index = alt_index + 1 + + $('#remove-alt_name-row').click -> + if (alt_index > 2) + alt_index = alt_index - 1 + tmp = 'alt_template[' + alt_index + ']' + element = document.getElementById(tmp) + console.log("%s",tmp) + element.remove() \ No newline at end of file diff --git a/app/assets/javascripts/finish_seed.js.coffee b/app/assets/javascripts/finish_seed.js.coffee new file mode 100644 index 000000000..53a878963 --- /dev/null +++ b/app/assets/javascripts/finish_seed.js.coffee @@ -0,0 +1,19 @@ +# Clears the finished at date field when +# a seed is marked unfinished, and +# repopulates the field with a cached value +# marking unfinished is undone. + +jQuery -> + previousValue = '' + $('#seed_finished').on('click', -> + finished = $('#seed_finished_at') + if @checked + if previousValue.length + date = previousValue + finished.val(date) + else + finished.trigger('focus') + else + previousValue = finished.val() + finished.val('') + ) diff --git a/app/assets/javascripts/seeds.js.coffee b/app/assets/javascripts/seeds.js.coffee index 6cbfb9eb1..ab5b73462 100644 --- a/app/assets/javascripts/seeds.js.coffee +++ b/app/assets/javascripts/seeds.js.coffee @@ -4,41 +4,3 @@ jQuery -> $('.add-datepicker').datepicker('format' : 'yyyy-mm-dd') - $('#add-sci_name-row').css("display", "inline-block") - $('#remove-sci_name-row').css("display", "inline-block") - $("#add-alt_name-row").css("display", "inline-block") - $("#remove-alt_name-row").css("display", "inline-block") - -$ -> - sci_template = "
Scientific name of crop.
" - - sci_index = $('#scientific_names .template').length + 1 - - $('#add-sci_name-row').click -> - compiled_input = $(sci_template.split("INDEX").join(sci_index)) - $('#scientific_names').append(compiled_input) - sci_index = sci_index + 1 - - $('#remove-sci_name-row').click -> - if (sci_index > 2) - sci_index = sci_index - 1 - tmp = 'sci_template[' + sci_index + ']' - element = document.getElementById(tmp) - element.remove() - - alt_template = "
Alternate name of crop.
" - - alt_index = $('#alternate_names .template').length + 1 - - $('#add-alt_name-row').click -> - compiled_input = $(alt_template.split("INDEX").join(alt_index)) - $('#alternate_names').append(compiled_input) - alt_index = alt_index + 1 - - $('#remove-alt_name-row').click -> - if (alt_index > 2) - alt_index = alt_index - 1 - tmp = 'alt_template[' + alt_index + ']' - element = document.getElementById(tmp) - console.log("%s",tmp) - element.remove() diff --git a/app/assets/stylesheets/bootstrap-accessibility.css b/app/assets/stylesheets/bootstrap-accessibility.css index b7de73ef0..abf715176 100644 --- a/app/assets/stylesheets/bootstrap-accessibility.css +++ b/app/assets/stylesheets/bootstrap-accessibility.css @@ -1 +1 @@ -.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-inner>.item{position:absolute;top:-999999em;display:block;-moz-transition:ease-in-out 0.6s left;-o-transition:ease-in-out 0.6s left;-webkit-transition:ease-in-out 0.6s left;transition:ease-in-out 0.6s left}.carousel-inner>.active{top:0}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{position:relative}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824} +.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-indicators li,.carousel-indicators li.active{height:18px;width:18px;border-width:2px;position:relative;box-shadow:0px 0px 0px 1px #808080}.carousel-indicators.active li{background-color:rgba(100,149,253,0.6)}.carousel-indicators.active li.active{background-color:white}.carousel-tablist-highlight{display:block;position:absolute;outline:2px solid transparent;background-color:transparent;box-shadow:0px 0px 0px 1px transparent}.carousel-tablist-highlight.focus{outline:2px solid #6495ED;background-color:rgba(0,0,0,0.4)}a.carousel-control:focus{outline:2px solid #6495ED;background-image:linear-gradient(to right, transparent 0px, rgba(0,0,0,0.5) 100%);box-shadow:0px 0px 0px 1px #000000}.carousel-pause-button{position:absolute;top:-30em;left:-300em;display:block}.carousel-pause-button.focus{top:0.5em;left:0.5em}.carousel:hover .carousel-caption,.carousel.contrast .carousel-caption{background-color:rgba(0,0,0,0.5);z-index:10}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824} diff --git a/app/assets/stylesheets/overrides.sass b/app/assets/stylesheets/overrides.sass index bdeb64d0d..338a2dbc5 100644 --- a/app/assets/stylesheets/overrides.sass +++ b/app/assets/stylesheets/overrides.sass @@ -94,21 +94,6 @@ p.stats dd margin-left: auto -@media (min-width: $screen-md-min) - .planting-thumbnail - dl.planting-attributes - width: 100% - - dt - text-align: left - width: 120px - dd - padding-left: 120px - margin-left: auto - - .navbar .navbar-form - width: 250px - #placesmap, #cropmap height: 500px @@ -254,24 +239,6 @@ html, body a font-weight: 800 -// Overrides applying only to mobile view. This must be at the end of the overrides file. - -@media only screen and (max-width: 767px) - .sidebar - margin-left: 0 - border-left: none - padding-left: 0 - - #map - height: 300px - - .navbar .nav > li - display: block - - .navbar .navbar-form - width: 185px - padding-left: 0 - padding-right: 0 /* override "info" alert boxes to be green, not blue, on Growstuff */ $state-info-text: darken($green, 10%) @@ -326,3 +293,44 @@ ul.thumbnail-buttons .hover-wrapper:hover .text visibility: visible + +.homepage-listing + padding-bottom: 6px + +@media (min-width: $screen-md-min) + .planting-thumbnail + dl.planting-attributes + width: 100% + + dt + text-align: left + width: 120px + dd + padding-left: 120px + margin-left: auto + + .navbar .navbar-form + width: 250px + +// Overrides applying only to mobile view. This must be at the end of the overrides file. +@media only screen and (max-width: 767px) + .sidebar + margin-left: 0 + border-left: none + padding-left: 0 + + #map + height: 300px + + .navbar .nav > li + display: block + + .navbar .navbar-form + width: 185px + padding-left: 0 + padding-right: 0 + .homepage + .thumbnail + height: 180px + .seed-thumbnail + height: 220px diff --git a/app/controllers/account_types_controller.rb b/app/controllers/account_types_controller.rb deleted file mode 100644 index f86892346..000000000 --- a/app/controllers/account_types_controller.rb +++ /dev/null @@ -1,53 +0,0 @@ -class AccountTypesController < ApplicationController - before_action :authenticate_member! - load_and_authorize_resource - respond_to :html - - # GET /account_types - def index - @account_types = AccountType.all.order(:name) - respond_with(@account_types) - end - - # GET /account_types/1 - def show - respond_with(@account_types) - end - - # GET /account_types/new - def new - @account_type = AccountType.new - respond_with(@account_type) - end - - # GET /account_types/1/edit - def edit - respond_with(@account_type) - end - - # POST /account_types - def create - @account_type = AccountType.new(account_type_params) - flash[:notice] = I18n.t('account_types.created') if @account_type.save - respond_with(@account_type) - end - - # PUT /account_types/1 - def update - flash[:notice] = I18n.t('account_types.updated') if @account_type.update(account_type_params) - respond_with(@account_type) - end - - # DELETE /account_types/1 - def destroy - @account_type.destroy - flash[:notice] = I18n.t('account_types.deleted') - respond_with(@account_type) - end - - private - - def account_type_params - params.require(:account_type).permit(:is_paid, :is_permanent_paid, :name) - end -end diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb deleted file mode 100644 index 040092aba..000000000 --- a/app/controllers/accounts_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -class AccountsController < ApplicationController - before_action :authenticate_member! - load_and_authorize_resource - respond_to :html - - # GET /accounts - def index - @accounts = Account.all.order(created_at: :desc) - respond_with(@accounts) - end - - # GET /accounts/1 - def show - respond_with(@account) - end - - # GET /accounts/1/edit - def edit; end - - # PUT /accounts/1 - def update - flash[:notice] = I18n.t('account.update') if @account.update(params[:account]) - respond_with(@account) - end - - private - - def account_params - params.require(:account).permit(:account_type_id, :member_id, :paid_until) - end -end diff --git a/app/controllers/admin/orders_controller.rb b/app/controllers/admin/orders_controller.rb deleted file mode 100644 index d4f727ec0..000000000 --- a/app/controllers/admin/orders_controller.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Admin - class OrdersController < ApplicationController - def index - authorize! :manage, :all - respond_to do |format| - format.html # index.html.haml - end - end - - def search - authorize! :manage, :all - @orders = Order.search(by: params[:search_by], for: params[:search_text]) - - if @orders.empty? - flash[:alert] = "Couldn't find order with #{params[:search_by]} = #{params[:search_text]}" - end - - respond_to do |format| - format.html # index.html.haml - end - end - end -end diff --git a/app/controllers/charts/crops_controller.rb b/app/controllers/charts/crops_controller.rb new file mode 100644 index 000000000..b5fe3d4a7 --- /dev/null +++ b/app/controllers/charts/crops_controller.rb @@ -0,0 +1,30 @@ +module Charts + class CropsController < ApplicationController + respond_to :json + + def sunniness + pie_chart_query 'sunniness' + end + + def planted_from + pie_chart_query 'planted_from' + end + + def harvested_for + @crop = Crop.find(params[:crop_id]) + render json: Harvest.joins(:plant_part) + .where(crop: @crop) + .group("plant_parts.name").count(:id) + end + + private + + def pie_chart_query(field) + @crop = Crop.find(params[:crop_id]) + render json: Planting.where(crop: @crop) + .where.not(field.to_sym => nil) + .where.not(field.to_sym => '') + .group(field.to_sym).count(:id) + end + end +end diff --git a/app/controllers/charts/gardens_controller.rb b/app/controllers/charts/gardens_controller.rb new file mode 100644 index 000000000..8b67bc106 --- /dev/null +++ b/app/controllers/charts/gardens_controller.rb @@ -0,0 +1,16 @@ +module Charts + class GardensController < ApplicationController + respond_to :json + def timeline + @data = [] + @garden = Garden.find(params[:garden_id]) + @garden.plantings.where.not(planted_at: nil) + .order(finished_at: :desc).each do |p| + # use finished_at if we have it, otherwise use predictions + finish = p.finished_at.presence || p.finish_predicted_at + @data << [p.crop.name, p.planted_at, finish] if finish.present? + end + render json: @data + end + end +end diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index d3bd5dcf9..67832c9b1 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -51,7 +51,11 @@ class CropsController < ApplicationController def show @crop = Crop.includes(:scientific_names, plantings: :photos).find(params[:id]) @posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page]) - respond_with(@crop) + # respond_with(@crop) + respond_to do |format| + format.html + format.json { render json: @crop.to_json(crop_json_fields) } + end end def new diff --git a/app/controllers/order_items_controller.rb b/app/controllers/order_items_controller.rb deleted file mode 100644 index 326cff21f..000000000 --- a/app/controllers/order_items_controller.rb +++ /dev/null @@ -1,36 +0,0 @@ -class OrderItemsController < ApplicationController - before_action :authenticate_member! - load_and_authorize_resource - respond_to :html - responders :flash - - # POST /order_items - def create - if params[:order_item][:price] - params[:order_item][:price] = params[:order_item][:price].to_f * 100 # convert to cents - end - - @order_item = OrderItem.new(order_item_params) - @order_item.order = current_member.current_order || Order.create(member_id: current_member.id) - - if @order_item.save - redirect_to @order_item.order, notice: 'Added item to your order.' - else - redirect_to shop_path, alert: errors - end - end - - private - - def errors - if @order_item.errors.empty? - "There was a problem with your order." - else - @order_item.errors.full_messages.to_sentence - end - end - - def order_item_params - params.require(:order_item).permit(:order_id, :price, :product_id, :quantity) - end -end diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb deleted file mode 100644 index 31c709148..000000000 --- a/app/controllers/orders_controller.rb +++ /dev/null @@ -1,90 +0,0 @@ -class OrdersController < ApplicationController - before_action :authenticate_member! - load_and_authorize_resource - - # GET /orders - def index - @orders = Order.by_member(current_member) - - respond_to do |format| - format.html # index.html.erb - end - end - - # GET /orders/1 - def show - respond_to do |format| - format.html # show.html.erb - end - end - - # GET /orders/new - def new - @order = Order.new - - respond_to do |format| - format.html # new.html.erb - end - end - - # checkout with PayPal - def checkout - respond_to do |format| - if @order.update_attributes(referral_code: params[:referral_code]) - response = EXPRESS_GATEWAY.setup_purchase( - @order.total, - items: @order.activemerchant_items, - currency: Rails.application.config.currency, - no_shipping: true, - ip: request.remote_ip, - return_url: complete_order_url, - cancel_return_url: shop_url - ) - format.html { redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token) } - else - format.html { render action: "show" } - end - end - end - - def complete - if params[:token] && params['PayerID'] - purchase = EXPRESS_GATEWAY.purchase( - @order.total, - currency: Rails.application.config.currency, - ip: request.remote_ip, - payer_id: params['PayerID'], - token: params[:token] - ) - if purchase.success? - @order.completed_at = Time.zone.now - @order.record_paypal_details(params[:token]) - else - flash[:alert] = "Could not complete your order. Please notify support." - end - else - flash[:alert] = "PayPal didn't return a token or payer_id for your order. Please notify support." - end - - @order.update_account # apply paid account benefits, etc. - - respond_to do |format| - format.html # new.html.erb - end - end - - def cancel - respond_to do |format| - format.html { redirect_to shop_url, notice: 'Order was cancelled.' } - end - end - - # DELETE /orders/1 - def destroy - @order.destroy - - respond_to do |format| - format.html { redirect_to shop_url, notice: 'Order was deleted.' } - end - end -end diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index 2866c1549..57b10e5d8 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -88,7 +88,7 @@ class PhotosController < ApplicationController photo = Photo.find_by(flickr_photo_id: flickr_photo_id_param) photo ||= Photo.new(photo_params) photo.owner_id = current_member.id - photo.set_flickr_metadata + photo.set_flickr_metadata! photo end diff --git a/app/controllers/plantings_controller.rb b/app/controllers/plantings_controller.rb index b0e339710..0fa013649 100644 --- a/app/controllers/plantings_controller.rb +++ b/app/controllers/plantings_controller.rb @@ -31,12 +31,13 @@ class PlantingsController < ApplicationController @planting = Planting.includes(:owner, :crop, :garden, :photos) .friendly .find(params[:id]) - @photos = @planting.photos.order(created_at: :desc).includes(:owner).paginate(page: params[:page]) + @photos = @planting.photos.order(date_taken: :desc).includes(:owner).paginate(page: params[:page]) respond_with @planting end def new @planting = Planting.new(planted_at: Time.zone.today) + @seed = Seed.find_by(slug: params[:seed_id]) if params[:seed_id] # using find_by_id here because it returns nil, unlike find @crop = Crop.approved.find_by(id: params[:crop_id]) || Crop.new @@ -54,7 +55,8 @@ class PlantingsController < ApplicationController def create @planting = Planting.new(planting_params) @planting.owner = current_member - @planting.save + @planting.crop = @planting.parent_seed.crop if @planting.parent_seed.present? + @planting.save! respond_with @planting end @@ -82,6 +84,7 @@ class PlantingsController < ApplicationController params[:planted_at] = parse_date(params[:planted_at]) if params[:planted_at] params.require(:planting).permit( :crop_id, :description, :garden_id, :planted_at, + :parent_seed_id, :quantity, :sunniness, :planted_from, :finished, :finished_at ) diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb deleted file mode 100644 index 8e0a1fb08..000000000 --- a/app/controllers/products_controller.rb +++ /dev/null @@ -1,46 +0,0 @@ -class ProductsController < ApplicationController - before_action :authenticate_member! - load_and_authorize_resource - respond_to :html - responders :flash - - def index - @products = Product.all.order(:name) - respond_with @products - end - - def show - respond_with @product - end - - def new - @product = Product.new - respond_with @product - end - - def edit - respond_with @product - end - - def create - @product = Product.create(product_params) - respond_with @product - end - - def update - @product.update(product_params) - respond_with @product - end - - def destroy - @product.destroy - respond_with @product - end - - private - - def product_params - params.require(:product).permit(:description, :min_price, :recommended_price, :name, - :account_type_id, :paid_months) - end -end diff --git a/app/controllers/seeds_controller.rb b/app/controllers/seeds_controller.rb index 12445877e..b18e6090a 100644 --- a/app/controllers/seeds_controller.rb +++ b/app/controllers/seeds_controller.rb @@ -16,44 +16,37 @@ class SeedsController < ApplicationController respond_with(@seeds) end - # GET /seeds/1 - # GET /seeds/1.json def show @photos = @seed.photos.includes(:owner).order(created_at: :desc).paginate(page: params[:page]) respond_with(@seed) end - # GET /seeds/new - # GET /seeds/new.json def new @seed = Seed.new - # using find_by_id here because it returns nil, unlike find - @crop = Crop.find_or_initialize_by(id: params[:crop_id]) + if params[:planting_id] + @planting = Planting.find_by(slug: params[:planting_id]) + else + @crop = Crop.find_or_initialize_by(id: params[:crop_id]) + end respond_with(@seed) end - # GET /seeds/1/edit def edit; end - # POST /seeds - # POST /seeds.json def create @seed = Seed.new(seed_params) @seed.owner = current_member + @seed.crop = @seed.parent_planting.crop if @seed.parent_planting flash[:notice] = "Successfully added #{@seed.crop} seed to your stash." if @seed.save respond_with(@seed) end - # PUT /seeds/1 - # PUT /seeds/1.json def update flash[:notice] = 'Seed was successfully updated.' if @seed.update(seed_params) respond_with(@seed) end - # DELETE /seeds/1 - # DELETE /seeds/1.json def destroy @seed.destroy respond_with(@seed) @@ -64,8 +57,11 @@ class SeedsController < ApplicationController def seed_params params.require(:seed).permit( :crop_id, :description, :quantity, :plant_before, - :days_until_maturity_min, :days_until_maturity_max, :organic, :gmo, - :heirloom, :tradable_to, :slug + :parent_planting_id, + :days_until_maturity_min, :days_until_maturity_max, + :organic, :gmo, + :heirloom, :tradable_to, :slug, + :finished, :finished_at ) end diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop_controller.rb deleted file mode 100644 index 964511e50..000000000 --- a/app/controllers/shop_controller.rb +++ /dev/null @@ -1,19 +0,0 @@ -class ShopController < ApplicationController - respond_to :html - def index - @products = Product.all - @order_item = OrderItem.new - - # this is (hopefully) part of a short-term hack to prevent people from - # ordering multiple subscriptions, which would be very confusing to deal - # with. We check whether they have an order already in progress, and if - # so, point that out to them and encourage them to checkout, rather than - # letting them add more stuff to their order. - - @order = nil - @most_recent_item = nil - return unless current_member - @order = current_member.current_order - @most_recent_item = @order.order_items.first if @order - end -end diff --git a/app/helpers/harvests_helper.rb b/app/helpers/harvests_helper.rb index 14277817b..10ed30888 100644 --- a/app/helpers/harvests_helper.rb +++ b/app/helpers/harvests_helper.rb @@ -28,7 +28,12 @@ module HarvestsHelper end def display_harvest_description(harvest) - return "No description provided." if harvest.description.nil? - harvest.description + if harvest.description.nil? + "no description provided." + else + truncate(harvest.description, length: 50, separator: ' ', omission: '... ') do + link_to "Read more", harvest_path(harvest) + end + end end end diff --git a/app/helpers/photos_helper.rb b/app/helpers/photos_helper.rb new file mode 100644 index 000000000..b77f35bd9 --- /dev/null +++ b/app/helpers/photos_helper.rb @@ -0,0 +1,51 @@ +module PhotosHelper + def crop_image_path(crop) + if crop.default_photo.present? + crop.default_photo.thumbnail_url + else + placeholder_image + end + end + + def garden_image_path(garden) + if garden.default_photo.present? + garden.default_photo.thumbnail_url + else + placeholder_image + end + end + + def planting_image_path(planting) + if planting.photos.present? + planting.photos.first.thumbnail_url + else + placeholder_image + end + end + + def harvest_image_path(harvest) + if harvest.photos.present? + harvest.photos.first.thumbnail_url + elsif harvest.planting.present? && harvest.planting.photos.present? + harvest.planting.photos.first.thumbnail_url + else + placeholder_image + end + end + + def seed_image_path(seed) + if seed.default_photo.present? + seed.default_photo.thumbnail_url + elsif seed.crop.default_photo.present? + seed.crop.default_photo.thumbnail_url + else + placeholder_image + end + end + + private + + def placeholder_image + 'placeholder_150.png' + end +end diff --git a/app/models/ability.rb b/app/models/ability.rb index 283bf45cc..4fd14340e 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -24,14 +24,9 @@ class Ability # except these, which don't make sense if you're not logged in cannot :read, Notification cannot :read, Authentication - cannot :read, Order - cannot :read, OrderItem # and nobody should be able to view this except admins cannot :read, Role - cannot :read, Product - cannot :read, Account - cannot :read, AccountType # nobody should be able to view unapproved crops unless they # are wranglers or admins @@ -116,23 +111,12 @@ class Ability can :update, Photo, owner_id: member.id can :destroy, Photo, owner_id: member.id - can :create, Seed - can :update, Seed, owner_id: member.id + can :create, Seed + can :update, Seed, owner_id: member.id can :destroy, Seed, owner_id: member.id - - # orders/shop/etc - can :create, Order - can :read, Order, member_id: member.id - can :complete, Order, member_id: member.id, completed_at: nil - can :checkout, Order, member_id: member.id, completed_at: nil - can :cancel, Order, member_id: member.id, completed_at: nil - can :destroy, Order, member_id: member.id, completed_at: nil - - can :create, OrderItem - # for now, let's not let people mess with individual order items - cannot :read, OrderItem, order: { member_id: member.id } - cannot :update, OrderItem, order: { member_id: member.id, completed_at: nil } - cannot :destroy, OrderItem, order: { member_id: member.id, completed_at: nil } + can :create, Seed, owner_id: member.id, parent_planting: { owner_id: member.id } + can :update, Seed, owner_id: member.id, parent_planting: { owner_id: member.id } + can :destroy, Seed, owner_id: member.id, parent_planting: { owner_id: member.id } # following/unfollowing permissions can :create, Follow @@ -148,12 +132,6 @@ class Ability can :read, :all can :manage, :all - # can't change order history, because it's *history* - cannot :create, Order - cannot :complete, Order - cannot :destroy, Order - cannot :manage, OrderItem - # can't delete plant parts if they have harvests associated with them cannot :destroy, PlantPart can :destroy, PlantPart do |pp| diff --git a/app/models/account.rb b/app/models/account.rb deleted file mode 100644 index be5b1e9cf..000000000 --- a/app/models/account.rb +++ /dev/null @@ -1,24 +0,0 @@ -class Account < ApplicationRecord - belongs_to :member - belongs_to :account_type - - validates :member_id, uniqueness: { - message: 'already has account details associated with it' - } - - before_validation do - # If not account type, set to the free account - unless account_type - self.account_type = AccountType.find_or_create_by(name: - Rails.application.config.default_account_type) - end - end - - def paid_until_string - if account_type.is_permanent_paid - "forever" - elsif account_type.is_paid - paid_until.to_s - end - end -end diff --git a/app/models/account_type.rb b/app/models/account_type.rb deleted file mode 100644 index c2294ca53..000000000 --- a/app/models/account_type.rb +++ /dev/null @@ -1,13 +0,0 @@ -class AccountType < ApplicationRecord - # - # Relationships - has_many :products - - # - # Validations - validates :name, presence: true, uniqueness: true - - def to_s - name - end -end diff --git a/app/models/concerns/finishable.rb b/app/models/concerns/finishable.rb new file mode 100644 index 000000000..42fa4e734 --- /dev/null +++ b/app/models/concerns/finishable.rb @@ -0,0 +1,12 @@ +module Finishable + extend ActiveSupport::Concern + + included do + scope :finished, -> { where(finished: true) } + scope :current, -> { where.not(finished: true) } + + def active? + !finished + end + end +end diff --git a/app/models/crop.rb b/app/models/crop.rb index a5105fb94..9f1ec6e59 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -120,12 +120,7 @@ class Crop < ApplicationRecord # later we can choose a default photo based on different criteria, # eg. popularity def default_photo - # most recent photo - return photos.order(created_at: :desc).first if photos.any? - - # Crop has no photos? Look for the most recent harvest with a photo. - harvest_with_photo = Harvest.where(crop_id: id).joins(:photos).order('harvests.id DESC').limit(1).first - harvest_with_photo.photos.first if harvest_with_photo + first_photo(:plantings) || first_photo(:harvests) || first_photo(:seeds) end # returns hash indicating whether this crop is grown in @@ -242,4 +237,8 @@ class Crop < ApplicationRecord return unless reason_for_rejection == "other" && rejection_notes.blank? errors.add(:rejection_notes, "must be added if the reason for rejection is \"other\"") end + + def first_photo(type) + Photo.joins(type).where("#{type}": { crop_id: id }).order("photos.created_at DESC").first + end end diff --git a/app/models/harvest.rb b/app/models/harvest.rb index a58eaa991..60a43ce44 100644 --- a/app/models/harvest.rb +++ b/app/models/harvest.rb @@ -40,6 +40,13 @@ class Harvest < ApplicationRecord ## ## Scopes default_scope { joins(:owner) } # Ensures owner exists + scope :interesting, -> { has_photos.one_per_owner } + scope :recent, -> { order(created_at: :desc) } + scope :one_per_owner, lambda { + joins("JOIN members m ON (m.id=harvests.owner_id) + LEFT OUTER JOIN harvests h2 + ON (m.id=h2.owner_id AND harvests.id < h2.id)").where("h2 IS NULL") + } ## ## Validations diff --git a/app/models/member.rb b/app/models/member.rb index 7165faf4f..f2427d45d 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -7,7 +7,7 @@ class Member < ApplicationRecord friendly_id :login_name, use: %i(slugged finders) # - # Relationshops + # Relationships has_many :posts, foreign_key: 'author_id' has_many :comments, foreign_key: 'author_id' has_many :forums, foreign_key: 'owner_id' @@ -19,9 +19,6 @@ class Member < ApplicationRecord has_many :notifications, foreign_key: 'recipient_id' has_many :sent_notifications, foreign_key: 'sender_id' has_many :authentications - has_many :orders - has_one :account - has_one :account_type, through: :account has_many :photos has_many :requested_crops, class_name: Crop, foreign_key: 'requester_id' has_many :likes, dependent: :destroy @@ -77,7 +74,11 @@ class Member < ApplicationRecord after_validation :geocode after_validation :empty_unwanted_geocodes after_save :update_newsletter_subscription - after_create :create_account_and_garden + + # Give each new member a default garden + # we use find_or_create to avoid accidentally creating a second one, + # which can happen sometimes especially with FactoryBot associations + after_create { |member| Garden.create(name: "Garden", owner_id: member.id) } # allow login via either login_name or email address def self.find_first_by_auth_conditions(warden_conditions) @@ -95,34 +96,6 @@ class Member < ApplicationRecord roles.any? { |r| r.name.gsub(/\s+/, "_").underscore.to_sym == role_sym } end - def current_order - orders.find_by(completed_at: nil) - end - - # when purchasing a product that gives you a paid account, this method - # does all the messing around to actually make sure the account is - # updated correctly -- account type, paid until, etc. Usually this is - # called by order.update_account, which loops through all order items - # and does this for each one. - def update_account_after_purchase(product) - account.account_type = product.account_type if product.account_type - if product.paid_months - start_date = account.paid_until || Time.zone.now - account.paid_until = start_date + product.paid_months.months - end - account.save - end - - def paid? - if account.account_type.is_permanent_paid - true - elsif account.account_type.is_paid && account.paid_until >= Time.zone.now - true - else - false - end - end - def auth(provider) authentications.find_by(provider: provider) end @@ -232,13 +205,4 @@ class Member < ApplicationRecord def get_follow(member) follows.find_by(followed_id: member.id) if already_following?(member) end - - # Give each new member a default garden - # and an account record (for paid accounts etc) - # we use find_or_create to avoid accidentally creating a second one, - # which can happen sometimes especially with FactoryBot associations - def create_account_and_garden - Garden.create!(name: "Garden", owner_id: id) - Account.find_or_create_by!(member_id: id) - end end diff --git a/app/models/order.rb b/app/models/order.rb deleted file mode 100644 index fe5f65888..000000000 --- a/app/models/order.rb +++ /dev/null @@ -1,90 +0,0 @@ -class Order < ApplicationRecord - # - # Relationships - belongs_to :member, -> { with_deleted } - has_many :order_items, dependent: :destroy - - # - # Validations - validates :referral_code, format: { - with: /\A[a-zA-Z0-9 ]*\z/, - message: "may only include letters and numbers" - } - - # - # Teiggers - before_save :standardize_referral_code - - # - # Scopes - scope :by_member, ->(member) { where(member: member) } - - # total price of an order - def total - sum = 0 - order_items.each do |i| - subtotal = i.price * i.quantity - sum += subtotal - end - sum - end - - # return items in the format ActiveMerchant/PayPal want them - def activemerchant_items - items = [] - order_items.each do |i| - items.push(name: i.product.name, - quantity: i.quantity, - amount: i.price) - end - items - end - - # record the paypal details for reference - def record_paypal_details(token) - self.paypal_express_token = token - details = EXPRESS_GATEWAY.details_for(token) - self.paypal_express_payer_id = details.payer_id - save - end - - # when an order is completed, we update the member's account to mark - # them as paid, or whatever, based on what products they ordered - def update_account - order_items.each do |i| - member.update_account_after_purchase(i.product) - end - end - - # removes whitespace and forces to uppercase (we're somewhat liberal - # in what we accept, but we clean it up anyway.) - def standardize_referral_code - self.referral_code = referral_code.upcase.gsub(/\s/, '') if referral_code - end - - # search orders (used by admin/orders) - # usage: Order.search({ :by => 'member', :for => 'Skud' }) - # can search by: member, order_id, paypal_token, paypal_payer_id, - def self.search(args = {}) - if args[:for] - case args[:by] - when "member" - member = Member.with_deleted.find_by(login_name: args[:for]) - return member.orders if member - when "order_id" - order = Order.find_by(id: args[:for]) - return [order] if order - when "paypal_token" - order = Order.find_by(paypal_express_token: args[:for]) - return [order] if order - when "paypal_payer_id" - order = Order.find_by(paypal_express_payer_id: args[:for]) - return [order] if order - when "referral_code" - # coerce to uppercase - return Order.where(referral_code: args[:for].upcase) - end - end - [] - end -end diff --git a/app/models/order_item.rb b/app/models/order_item.rb deleted file mode 100644 index 771919b55..000000000 --- a/app/models/order_item.rb +++ /dev/null @@ -1,12 +0,0 @@ -class OrderItem < ApplicationRecord - belongs_to :order - belongs_to :product - - validate :price_must_be_greater_than_minimum - validates :order_id, uniqueness: { message: "may only have one item." } - - def price_must_be_greater_than_minimum - @product = Product.find(product_id) - errors.add(:price, "must be greater than the product's minimum value") if price < @product.min_price - end -end diff --git a/app/models/photo.rb b/app/models/photo.rb index 0daae7825..c9c8c6a94 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -27,7 +27,8 @@ class Photo < ApplicationRecord license_url: license.url, thumbnail_url: FlickRaw.url_q(info), fullsize_url: FlickRaw.url_z(info), - link_url: FlickRaw.url_photopage(info) + link_url: FlickRaw.url_photopage(info), + date_taken: info.dates.taken } end @@ -49,7 +50,11 @@ class Photo < ApplicationRecord end end - def set_flickr_metadata + def set_flickr_metadata! update_attributes(flickr_metadata) end + + def to_s + "#{title} by #{owner.login_name}" + end end diff --git a/app/models/planting.rb b/app/models/planting.rb index 32f9d3786..f985565a7 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -1,6 +1,7 @@ class Planting < ApplicationRecord extend FriendlyId include PhotoCapable + include Finishable friendly_id :planting_slug, use: %i(slugged finders) # Constants @@ -20,11 +21,15 @@ class Planting < ApplicationRecord belongs_to :crop, counter_cache: true has_many :harvests, dependent: :destroy + # + # Ancestry of food + belongs_to :parent_seed, class_name: 'Seed', foreign_key: 'parent_seed_id' # parent + has_many :child_seeds, class_name: 'Seed', + foreign_key: 'parent_planting_id', dependent: :nullify # children + ## ## Scopes default_scope { joins(:owner) } # Ensures the owner still exists - scope :finished, -> { where(finished: true) } - scope :current, -> { where(finished: false) } scope :interesting, -> { has_photos.one_per_owner } scope :recent, -> { order(created_at: :desc) } scope :one_per_owner, lambda { diff --git a/app/models/product.rb b/app/models/product.rb deleted file mode 100644 index 3946645d3..000000000 --- a/app/models/product.rb +++ /dev/null @@ -1,15 +0,0 @@ -class Product < ApplicationRecord - # - # Relationships - belongs_to :account_type - has_and_belongs_to_many :orders # rubocop:disable Rails/HasAndBelongsToMany - - # - # Validations - validates :paid_months, allow_nil: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } - validates :min_price, presence: true - - def to_s - name - end -end diff --git a/app/models/seed.rb b/app/models/seed.rb index ca99c4fc3..dff707a5c 100644 --- a/app/models/seed.rb +++ b/app/models/seed.rb @@ -1,6 +1,7 @@ class Seed < ApplicationRecord extend FriendlyId include PhotoCapable + include Finishable friendly_id :seed_slug, use: %i(slugged finders) TRADABLE_TO_VALUES = %w(nowhere locally nationally internationally).freeze @@ -13,11 +14,13 @@ class Seed < ApplicationRecord belongs_to :crop belongs_to :owner, class_name: 'Member', foreign_key: 'owner_id', counter_cache: true + belongs_to :parent_planting, class_name: 'Planting', foreign_key: 'parent_planting_id' # parent + has_many :child_plantings, class_name: 'Planting', + foreign_key: 'parent_seed_id', dependent: :nullify # children + # # Validations validates :crop, approved: true - delegate :name, to: :crop - delegate :default_photo, to: :crop validates :crop, presence: { message: "must be present and exist in our database" } validates :quantity, allow_nil: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } @@ -38,6 +41,10 @@ class Seed < ApplicationRecord inclusion: { in: HEIRLOOM_VALUES, message: "You must say whether the seeds"\ "are heirloom, hybrid, or unknown" } + # + # Delegations + delegate :name, to: :crop + # # Scopes default_scope { joins(:owner) } # Ensure owner exists @@ -45,6 +52,10 @@ class Seed < ApplicationRecord scope :interesting, -> { tradable.has_location } scope :has_location, -> { joins(:owner).where.not("members.location": nil) } + def default_photo + photos.order(created_at: :desc).first + end + def tradable? tradable_to != 'nowhere' end diff --git a/app/views/account_types/_form.html.haml b/app/views/account_types/_form.html.haml deleted file mode 100644 index 5bfbb539a..000000000 --- a/app/views/account_types/_form.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -= form_for @account_type do |f| - - if @account_type.errors.any? - #error_explanation - %h2 - = pluralize(@account_type.errors.size, "error") - prohibited this account_type from being saved: - %ul - - @account_type.errors.full_messages.each do |msg| - %li= msg - - .field - = f.label :name - = f.text_field :name - .field - = f.label :is_paid - = f.check_box :is_paid - .field - = f.label :is_permanent_paid - = f.check_box :is_permanent_paid - .actions - = f.submit 'Save' diff --git a/app/views/account_types/edit.html.haml b/app/views/account_types/edit.html.haml deleted file mode 100644 index c994c4ff1..000000000 --- a/app/views/account_types/edit.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- content_for :title, "Editing account type" - -= render 'form' - -= link_to 'Show', @account_type -\| -= link_to 'Back', account_types_path diff --git a/app/views/account_types/index.html.haml b/app/views/account_types/index.html.haml deleted file mode 100644 index 6af6d129d..000000000 --- a/app/views/account_types/index.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -- content_for :title, "Listing account types" - -%table - %tr - %th Name - %th Is paid - %th Is permanent paid - %th - %th - %th - - - @account_types.each do |account_type| - %tr - %td= account_type.name - %td= account_type.is_paid - %td= account_type.is_permanent_paid - %td= link_to 'Show', account_type - %td= link_to 'Edit', edit_account_type_path(account_type) - %td= link_to 'Destroy', account_type, method: :delete, data: { confirm: 'Are you sure?' } - -%br - -= link_to 'New Account type', new_account_type_path diff --git a/app/views/account_types/new.html.haml b/app/views/account_types/new.html.haml deleted file mode 100644 index 92d309715..000000000 --- a/app/views/account_types/new.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -- content_for :title, "New account type" - -= render 'form' - -= link_to 'Back', account_types_path diff --git a/app/views/account_types/show.html.haml b/app/views/account_types/show.html.haml deleted file mode 100644 index a3f7f690b..000000000 --- a/app/views/account_types/show.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -%p#notice= notice - -%p - %b Name: - = @account_type.name -%p - %b Is paid: - = @account_type.is_paid -%p - %b Is permanent paid: - = @account_type.is_permanent_paid - -= link_to 'Edit', edit_account_type_path(@account_type) -\| -= link_to 'Delete', @account_type, method: :delete, data: { confirm: 'Are you sure?' } -\| -= link_to 'Back', account_types_path diff --git a/app/views/accounts/_form.html.haml b/app/views/accounts/_form.html.haml deleted file mode 100644 index 410b717fa..000000000 --- a/app/views/accounts/_form.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -= form_for @account do |f| - - if @account.errors.any? - #error_explanation - %h2 - = pluralize(@account.errors.size, "error") - prohibited this account from being saved: - %ul - - @account.errors.full_messages.each do |msg| - %li= msg - - .field - = f.label :member_id - = f.number_field :member_id - .field - = f.label :account_type - = f.text_field :account_type - .field - = f.label :paid_until - = f.datetime_select :paid_until - .actions - = f.submit 'Save' diff --git a/app/views/accounts/edit.html.haml b/app/views/accounts/edit.html.haml deleted file mode 100644 index ef8d08801..000000000 --- a/app/views/accounts/edit.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- content_for :title, "Editing account" - -= render 'form' - -= link_to 'Show', @account -\| -= link_to 'Back', accounts_path diff --git a/app/views/accounts/index.html.haml b/app/views/accounts/index.html.haml deleted file mode 100644 index 8d929dd0f..000000000 --- a/app/views/accounts/index.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -- content_for :title, "Listing accounts" - -%table - %tr - %th Member - %th Account type - %th Paid until - %th - %th - %th - - - @accounts.each do |account| - %tr - %td= account.member_id - %td= account.account_type - %td= account.paid_until - %td= link_to 'Show', account - %td= link_to 'Edit', edit_account_path(account) - %td= link_to 'Destroy', account, method: :delete, data: { confirm: 'Are you sure?' } - -%br - -= link_to 'New Account detail', new_account_path diff --git a/app/views/accounts/new.html.haml b/app/views/accounts/new.html.haml deleted file mode 100644 index 9430cc2ed..000000000 --- a/app/views/accounts/new.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -- content_for :title, "New account" - -= render 'form' - -= link_to 'Back', accounts_path diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml deleted file mode 100644 index 5c5288674..000000000 --- a/app/views/accounts/show.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -%p#notice= notice - -%p - %b Member: - = @account.member_id -%p - %b Account type: - = @account.account_type.name -%p - %b Paid until: - = @account.paid_until - -= link_to 'Edit', edit_account_path(@account) -\| -= link_to 'Back', accounts_path diff --git a/app/views/admin/index.html.haml b/app/views/admin/index.html.haml index 389cc2dee..f82c5c336 100644 --- a/app/views/admin/index.html.haml +++ b/app/views/admin/index.html.haml @@ -6,8 +6,6 @@ .col-md-4 %h2 Site admin %ul#site_admin - %li= link_to "Account types", account_types_path - %li= link_to "Products", products_path %li= link_to "Roles", roles_path %li= link_to "Forums", forums_path -# %li= link_to "CMS", comfy_admin_cms_path @@ -22,8 +20,3 @@ %ul %li= link_to "Newsletter subscribers", admin_newsletter_path %li= link_to "Members", admin_members_path - -.row - .col-md-12 - %h2 Orders - = render "admin/orders/searchform" diff --git a/app/views/admin/members/index.html.haml b/app/views/admin/members/index.html.haml index 2408a55db..d91b1f8a1 100644 --- a/app/views/admin/members/index.html.haml +++ b/app/views/admin/members/index.html.haml @@ -11,5 +11,5 @@ %th - @members.each do |member| %tr - %td= ember.login_name + %td= member.login_name %td= member.email diff --git a/app/views/admin/orders/_searchform.html.haml b/app/views/admin/orders/_searchform.html.haml deleted file mode 100644 index e0a4dc22c..000000000 --- a/app/views/admin/orders/_searchform.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -= form_tag(url_for(controller: 'admin/orders', action: 'search'), method: :get, class: 'form-inline') do - = label_tag :distance, "Search orders:", class: 'control-label' - = text_field_tag :search_text - = select_tag :search_by, - options_for_select('Member': 'member', 'Referral code': 'referral_code', - 'Order ID': 'order_id', 'Paypal Token': 'paypal_token', - 'Paypal Payer ID': 'paypal_payer_id') - = submit_tag "Search", class: 'btn btn-primary' diff --git a/app/views/admin/orders/index.html.haml b/app/views/admin/orders/index.html.haml deleted file mode 100644 index e5f67f6c3..000000000 --- a/app/views/admin/orders/index.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -- content_for :title, 'Admin Orders' - -= render "admin/orders/searchform" diff --git a/app/views/admin/orders/search.html.haml b/app/views/admin/orders/search.html.haml deleted file mode 100644 index 23c93d01a..000000000 --- a/app/views/admin/orders/search.html.haml +++ /dev/null @@ -1,41 +0,0 @@ -- content_for :title, 'Search Orders' - -= render "admin/orders/searchform" - -- unless @orders.empty? - %h2 - Found - = pluralize(@orders.size, "result") - - %table.table.table-striped - %tr - %th Member - %th Order number - %th Date completed - %th Referral code - %th Items - %th - - - @orders.each do |order| - %tr - %td - = link_to order.member.login_name, order.member - = "(deleted)" if order.member.deleted_at - %td= order.id - %td - - if order.completed_at - = order.completed_at.to_s - - else - In progress - %td - = order.referral_code - %td - - unless order.order_items.empty? - - order.order_items.each do |o| - = o.quantity - x - = o.product.name - @ - = price_with_currency(o.price) - %br/ - %td= link_to 'Details', order, class: 'btn btn-default btn-xs' diff --git a/app/views/crops/_actions.html.haml b/app/views/crops/_actions.html.haml new file mode 100644 index 000000000..26f083d7b --- /dev/null +++ b/app/views/crops/_actions.html.haml @@ -0,0 +1,8 @@ +- if can? :create, Planting + = link_to "Plant this", new_planting_path(crop_id: crop.id), class: 'btn btn-default' + +- if can? :create, Harvest + = link_to "Harvest this", new_harvest_path(crop_id: crop.id), class: 'btn btn-default' + +- if can? :create, Seed + = link_to 'Add seeds to stash', new_seed_path(params: { crop_id: crop.id }), class: 'btn btn-default' diff --git a/app/views/crops/_image_with_popover.html.haml b/app/views/crops/_image_with_popover.html.haml index 6744ccfe1..a8ce256bd 100644 --- a/app/views/crops/_image_with_popover.html.haml +++ b/app/views/crops/_image_with_popover.html.haml @@ -1,5 +1,5 @@ - cache crop do - = link_to image_tag(crop.default_photo.present? ? crop.default_photo.thumbnail_url : 'placeholder_150.png', + = link_to image_tag(crop_image_path(crop), alt: crop.name, class: 'image-responsive crop-image'), crop.name, rel: "popover", diff --git a/app/views/crops/_index_card.html.haml b/app/views/crops/_index_card.html.haml index 39b9e8da4..99b4a1a7e 100644 --- a/app/views/crops/_index_card.html.haml +++ b/app/views/crops/_index_card.html.haml @@ -1,7 +1,7 @@ .well .row .col-md-4 - = link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), + = link_to image_tag(crop_image_path(crop), alt: '', class: 'img crop-image'), crop diff --git a/app/views/crops/_photos.html.haml b/app/views/crops/_photos.html.haml index 927bf97c8..b799876a9 100644 --- a/app/views/crops/_photos.html.haml +++ b/app/views/crops/_photos.html.haml @@ -8,7 +8,7 @@ - unless crop.photos.empty? %h3 Photos of #{crop.name} plants .row - - crop.photos.first(6).each do |p| + - crop.photos.order(date_taken: :desc).first(6).each do |p| .col-xs-6.col-md-2 = render "photos/thumbnail", photo: p .row diff --git a/app/views/crops/_thumbnail.html.haml b/app/views/crops/_thumbnail.html.haml index 1926b31d8..77793ef22 100644 --- a/app/views/crops/_thumbnail.html.haml +++ b/app/views/crops/_thumbnail.html.haml @@ -1,8 +1,8 @@ -.thumbnail - .crop-thumbnail - - if crop - - cache cache_key_for(Crop, crop.id) do - = link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), +- cache cache_key_for(Crop, crop.id) do + .thumbnail + .crop-thumbnail + - if crop + = link_to image_tag(crop_image_path(crop), alt: crop.name, class: 'img'), crop .cropinfo diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index 270c0db2b..7700501fa 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -8,18 +8,14 @@ = tag("meta", property: "og:url", content: request.original_url) = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) +- content_for :scripts do + = javascript_include_tag "charts" + = render partial: 'approval_status_message', locals: { crop: @crop } - if @crop.approved? - content_for :buttonbar do - - if can? :create, Planting - = link_to "Plant this", new_planting_path(crop_id: @crop.id), class: 'btn btn-default' - - - if can? :create, Harvest - = link_to "Harvest this", new_harvest_path(crop_id: @crop.id), class: 'btn btn-default' - - - if can? :create, Seed - = link_to 'Add seeds to stash', new_seed_path(params: { crop_id: @crop.id }), class: 'btn btn-default' + = render 'crops/actions', crop: @crop .row .col-md-9 diff --git a/app/views/gardens/_actions.html.haml b/app/views/gardens/_actions.html.haml index ecf434911..a5187991e 100644 --- a/app/views/gardens/_actions.html.haml +++ b/app/views/gardens/_actions.html.haml @@ -1,28 +1,29 @@ -- if can?(:edit, garden) || can?(:delete, garden) - - if can? :edit, garden - - if garden.active - = link_to new_planting_path(garden_id: garden.id), class: 'btn btn-primary' do - %span.glyphicon.glyphicon-grain{ title: "Plant" } - Plant something - = link_to "Mark as inactive", garden_path(garden, garden: { active: 0 }), - method: :put, class: 'btn btn-default', - data: { confirm: 'All plantings associated with this garden will be marked as finished. Are you sure?' } - - else - = link_to "Mark as active", garden_path(garden, garden: { active: 1 }), - method: :put, - class: 'btn btn-default' - = link_to edit_garden_path(garden), class: 'btn btn-default', id: 'edit_garden_link' do - %span.glyphicon.glyphicon-pencil{ title: "Edit garden" } - Edit +.garden-actions + - if can?(:edit, garden) + .btn-group + - if garden.active + = link_to new_planting_path(garden_id: garden.id), class: 'btn btn-default btn-xs' do + %span.glyphicon.glyphicon-grain{ title: "Plant" } + Plant something + = link_to "Mark as inactive", garden_path(garden, garden: { active: 0 }), + method: :put, class: 'btn btn-default btn-xs', + data: { confirm: 'All plantings associated with this garden will be marked as finished. Are you sure?' } + - else + = link_to "Mark as active", garden_path(garden, garden: { active: 1 }), + method: :put + = render 'shared/buttons/edit', path: edit_garden_path(garden) + + - if can?(:edit, garden) && can?(:create, Photo) + = link_to new_photo_path(type: "garden", id: garden.id), + class: 'btn btn-default btn-xs' do + %span.glyphicon.glyphicon-camera{ title: "Add photo" } + Add photo + - if can?(:destroy, garden) - = link_to garden, - method: :delete, - data: { confirm: 'All plantings associated with this garden will also be deleted. Are you sure?' }, - class: 'btn btn-default', id: 'delete_garden_link' do - %span.glyphicon.glyphicon-trash{ title: "Delete" } - Delete - - if can?(:edit, garden) && can?(:create, Photo) - = link_to new_photo_path(type: "garden", id: garden.id), - class: 'btn btn-primary' do - %span.glyphicon.glyphicon-camera{ title: "Add Photo" } - Add Photo + .pull-right + = link_to garden_path(garden), + method: :delete, + data: { confirm: 'All plantings associated with this garden will also be deleted. Are you sure?' }, + class: 'btn btn-default btn-xs', id: 'delete_garden_link' do + %span.glyphicon.glyphicon-trash{ title: "Delete" } + Delete diff --git a/app/views/gardens/_nav.haml b/app/views/gardens/_nav.haml index 82ae83086..36c828b87 100644 --- a/app/views/gardens/_nav.haml +++ b/app/views/gardens/_nav.haml @@ -4,12 +4,7 @@ = link_to "Everyone's gardens", gardens_path, class: 'btn btn-default' - - = link_to gardens_active_tickbox_path(@owner, show_all) do - = check_box_tag 'active', 'all', show_all - include in-active - -- if can?(:create, Garden) - = link_to 'Add a garden', new_garden_path, class: 'btn btn-primary' -- unless current_member - = render partial: 'shared/signin_signup', locals: { to: 'add a new garden' } + - if can?(:create, Garden) + = link_to 'Add a garden', new_garden_path, class: 'btn btn-default' + - unless current_member + = render 'shared/signin_signup', to: 'add a new garden' diff --git a/app/views/gardens/_overview.html.haml b/app/views/gardens/_overview.html.haml index 644115d16..b8f9ce54a 100644 --- a/app/views/gardens/_overview.html.haml +++ b/app/views/gardens/_overview.html.haml @@ -1,9 +1,7 @@ - .panel.panel-success .panel-heading %h3.panel-title = link_to garden.name, garden_path(garden) - .panel-body .row .col-md-2.col-xs-12.garden-info @@ -11,15 +9,17 @@ .col-md-12.col-xs-6 = render 'gardens/photo', garden: garden .col-md-12.col-xs-6 - = render 'gardens/actions', garden: garden + = display_garden_description(garden) .col-md-10 .row - if garden.plantings.current.size.positive? - garden.plantings.current.includes(:crop).each do |planting| .col-md-2.col-sm-6.col-xs-6 .hover-wrapper - .text= render 'plantings/actions', planting: planting + .text + = render 'plantings/actions', planting: planting = render partial: "plantings/thumbnail", locals: { planting: planting } - else - no plantings - -# .panel-footer + .col-md-2.col-sm-6.col-xs-6 no plantings + - if can?(:edit, garden) + .panel-footer= render 'gardens/actions', garden: garden diff --git a/app/views/gardens/_photo.html.haml b/app/views/gardens/_photo.html.haml index caf5c04d1..b6d301d2e 100644 --- a/app/views/gardens/_photo.html.haml +++ b/app/views/gardens/_photo.html.haml @@ -1,3 +1,3 @@ -= link_to image_tag((garden.default_photo ? garden.default_photo.thumbnail_url : 'placeholder_150.png'), += link_to image_tag(garden_image_path(garden), alt: garden.name, class: 'img-responsive'), garden_path(garden) diff --git a/app/views/gardens/_thumbnail.html.haml b/app/views/gardens/_thumbnail.html.haml index 56c8c9614..a2e4ea7eb 100644 --- a/app/views/gardens/_thumbnail.html.haml +++ b/app/views/gardens/_thumbnail.html.haml @@ -8,7 +8,7 @@ .panel-body{ id: "gardens_panel_body" } .row .col-md-4 - = link_to image_tag((garden.default_photo ? garden.default_photo.thumbnail_url : 'placeholder_150.png'), + = link_to image_tag(garden_image_path(garden), alt: garden.name, class: 'img'), garden_path(garden) .col-md-8 diff --git a/app/views/gardens/index.html.haml b/app/views/gardens/index.html.haml index 4cf4f0705..a09de73ff 100644 --- a/app/views/gardens/index.html.haml +++ b/app/views/gardens/index.html.haml @@ -1,6 +1,10 @@ - content_for :title, @owner ? "#{@owner}'s gardens" : "Everyone's gardens" -= render 'nav', owner: @owner, show_all: @show_all += render 'nav' + += link_to gardens_active_tickbox_path(@owner, @show_all) do + = check_box_tag 'active', 'all', @show_all + include in-active .pagination = page_entries_info @gardens diff --git a/app/views/gardens/show.html.haml b/app/views/gardens/show.html.haml index 3928cfa58..8b45b0b16 100644 --- a/app/views/gardens/show.html.haml +++ b/app/views/gardens/show.html.haml @@ -11,11 +11,13 @@ = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) - content_for :scripts do + = javascript_include_tag "charts" = javascript_include_tag "https://www.gstatic.com/charts/loader.js" .row .col-md-9 - %p.btn-group= render 'gardens/actions', garden: @garden + = render 'gardens/actions', garden: @garden + - unless @garden.active .alert.alert-warning @@ -25,20 +27,21 @@ to plant something in this garden. %div - :growstuff_markdown - #{strip_tags @garden.description} - - unless @garden.description - .row-fluid - %p No description available yet. + %p + :growstuff_markdown + #{strip_tags @garden.description} + - unless @garden.description + .row-fluid + %p No description available yet. - - if can? :edit, @garden - %p - Why not - = link_to 'tell us more.', edit_garden_path(@garden) + - if can? :edit, @garden + %p + Why not + = link_to 'tell us more.', edit_garden_path(@garden) %h3 Garden timeline .row - = timeline garden_timeline_path(@garden), adapter: "google" + .col-md-12= timeline garden_timeline_path(@garden), adapter: "google" %h3 Current plantings in garden .row diff --git a/app/views/harvests/_actions.html.haml b/app/views/harvests/_actions.html.haml new file mode 100644 index 000000000..5a2f1febc --- /dev/null +++ b/app/views/harvests/_actions.html.haml @@ -0,0 +1,6 @@ +- if can?(:edit, harvest) || can?(:destroy, harvest) + .btn-group.harvest-actions + - if can? :edit, harvest + = render 'shared/buttons/edit', path: edit_harvest_path(harvest) + - if can? :destroy, harvest + .pull-right= render 'shared/buttons/delete', path: harvest_path(harvest) diff --git a/app/views/harvests/_card.html.haml b/app/views/harvests/_card.html.haml index e82d53725..52cfb3627 100644 --- a/app/views/harvests/_card.html.haml +++ b/app/views/harvests/_card.html.haml @@ -8,7 +8,7 @@ .panel-body .row .col-md-4 - = link_to image_tag((harvest.default_photo ? harvest.default_photo.thumbnail_url : 'placeholder_150.png'), + = link_to image_tag(harvest_image_path(harvest), alt: harvest.crop.name, class: 'img'), harvest .col-md-8 @@ -21,9 +21,11 @@ %dd= display_quantity(harvest) %dt Harvest date : %dd= harvest.harvested_at - .panel-footer - %dt Description - %dd.truncate - = display_harvest_description(harvest) - = if harvest.description.present? - - link_to "Read more", harvest_path(harvest) + %dd Notes: + %dt=display_harvest_description(harvest) + - if harvest.planting.present? + %dt Harvested from + %dd= link_to(harvest.planting, planting_path(harvest.planting)) + .row + .col-md-12 + = render 'harvests/actions', harvest: harvest diff --git a/app/views/harvests/_image_with_popover.haml b/app/views/harvests/_image_with_popover.haml new file mode 100644 index 000000000..ebfb187f6 --- /dev/null +++ b/app/views/harvests/_image_with_popover.haml @@ -0,0 +1,9 @@ += link_to image_tag(harvest_image_path(harvest), + alt: harvest.to_s, + class: 'image-responsive crop-image'), + harvest, + rel: "popover", + 'data-trigger': 'hover', + 'data-title': harvest.to_s, + 'data-content': render('harvests/popover', harvest: harvest), + 'data-html': true diff --git a/app/views/harvests/_list.html.haml b/app/views/harvests/_list.html.haml new file mode 100644 index 000000000..bb0178238 --- /dev/null +++ b/app/views/harvests/_list.html.haml @@ -0,0 +1,11 @@ +- harvests.each do |h| + - cache h do + .row + .col-md-3.col-xs-4{ style: 'padding-bottom: 6px' } + = render 'harvests/image_with_popover', harvest: h + .col-md-9.col-xs-4 + = link_to h.crop, crop_path(h.crop) + %br/ + %small + %i + = h.owner.location diff --git a/app/views/harvests/_popover.html.haml b/app/views/harvests/_popover.html.haml new file mode 100644 index 000000000..508413918 --- /dev/null +++ b/app/views/harvests/_popover.html.haml @@ -0,0 +1,3 @@ +%p + %small + = harvest.harvested_at diff --git a/app/views/harvests/_thumbnail.html.haml b/app/views/harvests/_thumbnail.html.haml index 9a9beac0c..4344aed7d 100644 --- a/app/views/harvests/_thumbnail.html.haml +++ b/app/views/harvests/_thumbnail.html.haml @@ -1,9 +1,10 @@ .thumbnail .harvest-thumbnail - if harvest - = link_to image_tag((harvest.default_photo ? harvest.default_photo.thumbnail_url : 'placeholder_150.png'), + = link_to image_tag(harvest_image_path(harvest), alt: harvest.crop.name, class: 'img'), harvest .harvestinfo .harvest-name = link_to harvest, harvest + = I18n.l(harvest.harvested_at.to_date) diff --git a/app/views/harvests/show.html.haml b/app/views/harvests/show.html.haml index 2f9114084..a584389fe 100644 --- a/app/views/harvests/show.html.haml +++ b/app/views/harvests/show.html.haml @@ -32,13 +32,7 @@ %b Quantity: = display_quantity(@harvest) - - if can?(:edit, @harvest) || can?(:destroy, @harvest) - %p - - if can? :edit, @harvest - = link_to 'Edit', edit_harvest_path(@harvest), class: 'btn btn-default btn-xs' - - if can? :destroy, @harvest - = link_to 'Delete', @harvest, method: :delete, data: { confirm: 'Are you sure?' }, - class: 'btn btn-default btn-xs' + = render 'harvests/actions', harvest: @harvest .col-md-6 = render partial: "crops/index_card", locals: { crop: @harvest.crop } diff --git a/app/views/home/_crops.html.haml b/app/views/home/_crops.html.haml index 6f881dc32..bfcbc9a71 100644 --- a/app/views/home/_crops.html.haml +++ b/app/views/home/_crops.html.haml @@ -1,28 +1,5 @@ -.row - .col-md-8 - - cache cache_key_for(Crop, 'interesting'), expires_in: 1.day do - %h2= t('.our_crops') - .hidden-xs - - Crop.interesting.includes(:scientific_names, :photos).first(8).each do |c| - .col-md-3 - = render partial: 'crops/thumbnail', locals: { crop: c } - .visible-xs - - Crop.interesting.includes(:scientific_names, :photos).first(3).each do |c| - .col-md-3 - = render partial: 'crops/thumbnail', locals: { crop: c } - - .col-md-4.hidden-xs - - cache cache_key_for(Planting) do - %h2= t('.recently_planted') - = render 'plantings/list', plantings: Planting.includes(:owner, :photos).interesting.recent.first(6) - -.row - .col-md-12 - - cache cache_key_for(Crop, 'recent') do - %p{ style: 'margin-top: 11.25px' } - %strong - #{t('.recently_added')}: - != Crop.recent.limit(12).map { |c| link_to(c, c) }.join(", ") - - %p.text-right - = link_to "#{t('.view_all')} »", crops_path +- cache cache_key_for(Crop, 'interesting'), expires_in: 1.day do + .row + %h2= t('.our_crops') + - Crop.interesting.includes(:scientific_names, :photos).limit(8).each do |c| + .col-md-4.col-sm-3.col-xs-6= render 'crops/thumbnail', crop: c diff --git a/app/views/home/_discuss.html.haml b/app/views/home/_discuss.html.haml index 36cc1d0f9..0ec3077e1 100644 --- a/app/views/home/_discuss.html.haml +++ b/app/views/home/_discuss.html.haml @@ -1,6 +1,6 @@ %h2= t('.discussion') -- posts = Post.limit(6) +- posts = Post.order(created_at: :desc).limit(6) - if posts = render "posts/summary", posts: posts, howmany: 6 diff --git a/app/views/home/_harvests.html.haml b/app/views/home/_harvests.html.haml new file mode 100644 index 000000000..d4c2a1117 --- /dev/null +++ b/app/views/home/_harvests.html.haml @@ -0,0 +1,3 @@ +- cache cache_key_for(Harvest) do + %h2 Recently Harvested + = render 'harvests/list', harvests: Harvest.includes(:crop, :owner, :photos).has_photos.recent.first(5) diff --git a/app/views/home/_members.html.haml b/app/views/home/_members.html.haml index 0c786ca7b..c3a4392ba 100644 --- a/app/views/home/_members.html.haml +++ b/app/views/home/_members.html.haml @@ -1,13 +1,13 @@ - cache cache_key_for(Member) do .hidden-xs - - members = Member.interesting.first(6) + - members = Member.includes(plantings: :crop).interesting.first(6) - if members.present? %section %h2= t('.title') .member-cards - members.each do |m| - = render partial: "members/thumbnail", locals: { member: m } + = render "members/thumbnail", member: m %p.text-right = link_to "#{t('.view_all')} »", members_path diff --git a/app/views/home/_plantings.html.haml b/app/views/home/_plantings.html.haml new file mode 100644 index 000000000..595ad4586 --- /dev/null +++ b/app/views/home/_plantings.html.haml @@ -0,0 +1,3 @@ +- cache cache_key_for(Planting, 'home'), expires_in: 1.day do + %h2= t('.recently_planted') + = render 'plantings/list', plantings: Planting.includes(:crop, garden: :owner).has_photos.recent.limit(5) diff --git a/app/views/home/_seeds.html.haml b/app/views/home/_seeds.html.haml index 7469b90b0..d53759738 100644 --- a/app/views/home/_seeds.html.haml +++ b/app/views/home/_seeds.html.haml @@ -1,21 +1,22 @@ - cache cache_key_for(Seed, 'interesting'), expires_in: 1.day do %h2= t('.title') .row - .col-md-8 - - Seed.includes(:owner, crop: :photos).order(created_at: :desc).interesting.first(6).each do |seed| - .col-md-3 - .thumbnail - - cache cache_key_for(Crop, seed.id) do - = link_to image_tag((seed.default_photo ? seed.default_photo.thumbnail_url : 'placeholder_150.png'), - alt: seed.crop.name, class: 'img'), - seed - .seedinfo - = link_to seed.crop.name, seed - .trade-to - %p= seed.owner.location - %p + - Seed.current.tradable.order(created_at: :desc).limit(6).each do |seed| + .col-md-2.col-sm-2.col-xs-6 + .thumbnail.seed-thumbnail + - cache cache_key_for(Crop, seed.id) do + = link_to image_tag(seed_image_path(seed), + alt: seed.crop.name, class: 'img'), + seed + .seedinfo + = link_to seed.crop.name, seed + .trade-to + %p= seed.owner.location + %p + %small Will trade to: %br/ - #{seed.tradable_to} + %em= seed.tradable_to + %p.text-right = link_to "#{t('.view_all')} »", seeds_path diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 655fbd3fc..c28a8985b 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,9 +1,9 @@ -.row +.homepage.row .col-md-12 - if member_signed_in? %h1= t('.welcome', site_name: ENV['GROWSTUFF_SITE_NAME'], member_name: current_member) - = render partial: 'stats' + = render 'stats' %p .btn-group = link_to t('.plant'), new_planting_path, class: 'btn btn-default' @@ -14,12 +14,30 @@ - else .hidden-xs .jumbotron - = render partial: 'blurb' + = render 'blurb' .visible-xs - = render partial: 'blurb' + = render 'blurb' - = render partial: 'crops' - = render partial: 'seeds' - = render partial: 'members' - = render partial: 'discuss' + .row + .col-md-6.col-sm-12 + = render 'crops' + .col-md-3.col-sm-6 + = render 'plantings' + .col-md-3.col-sm-6 + = render 'harvests' + .col-md-12 + - cache cache_key_for(Crop, 'recent') do + %p{ style: 'margin-top: 11.25px' } + %strong + #{t('.recently_added')}: + != Crop.recent.limit(30).map { |c| link_to(c, c) }.join(", ") + %p.text-right + = link_to "#{t('home.crops.view_all')} »", crops_path + .row + .col-md-12 + = render 'seeds' + = render 'members' + .row + .col-md-12 + = render 'discuss' diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 3eb0d8ca0..d2b8fd01c 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -47,7 +47,6 @@ %li= link_to t('.browse_members'), members_path %li= link_to t('.posts'), posts_path %li= link_to t('.forums'), forums_path - %li= link_to t('.support_growstuff'), shop_path - if member_signed_in? %li.dropdown< @@ -64,7 +63,6 @@ %li= link_to t('.harvest'), harvests_by_owner_path(owner: current_member.slug) %li= link_to t('.seeds'), seeds_by_owner_path(owner: current_member.slug) %li= link_to t('.posts'), posts_by_author_path(author: current_member.slug) - %li= link_to t('.account'), orders_path %li - if current_member.notifications.unread_count.positive? = link_to(t('.inbox_unread', unread_count: current_member.notifications.unread_count), diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index f96e3f22c..6eea27068 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -17,9 +17,7 @@ %small= yield(:subtitle) - if content_for?(:buttonbar) - %p - .btn-group - = yield(:buttonbar) + .btn-group.layout-actions= yield(:buttonbar) = render partial: "shared/flash_messages", flash: flash = yield @@ -29,4 +27,5 @@ Javascripts \================================================== / Placed at the end of the document so the pages load faster + = javascript_include_tag "application" != Growstuff::Application.config.analytics_code diff --git a/app/views/members/_account.html.haml b/app/views/members/_account.html.haml deleted file mode 100644 index cda792ace..000000000 --- a/app/views/members/_account.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -%h3 Account details - -%p - %strong Member since: - = member.created_at.to_s(:date) - -%p - %strong Account type: - = member.account_type - account - -%p - %strong Last Login: - = member.last_sign_in_at - -%p - %strong Member Roles: - %br - - if member.role? :admin - Administrator - - if member.role? :crop_wrangler - Crop Wrangler - - else - Member diff --git a/app/views/members/_roles.html.haml b/app/views/members/_roles.html.haml new file mode 100644 index 000000000..cbfd0b576 --- /dev/null +++ b/app/views/members/_roles.html.haml @@ -0,0 +1,9 @@ +%p + %strong Member Roles: + %br + - if member.role? :admin + Administrator + - if member.role? :crop_wrangler + Crop Wrangler + - unless (member.role?(:admin) || member.role?(:crop_wrangler)) + Member diff --git a/app/views/members/_stats.html.haml b/app/views/members/_stats.html.haml index 346d71296..a2ebab47d 100644 --- a/app/views/members/_stats.html.haml +++ b/app/views/members/_stats.html.haml @@ -1,3 +1,10 @@ +%p + %strong Member since: + = member.created_at.to_s(:date) +%p + %strong Last Login: + = member.last_sign_in_at + %h3 Activity %ul.list-inline @@ -33,4 +40,3 @@ = link_to pluralize(member.followers.size, "follower"), member_followers_path(member) - else 0 followers - diff --git a/app/views/members/show.html.haml b/app/views/members/show.html.haml index f0214a52f..478574b3b 100644 --- a/app/views/members/show.html.haml +++ b/app/views/members/show.html.haml @@ -10,8 +10,6 @@ - content_for :buttonbar do - if can? :update, @member = link_to 'Edit profile', edit_member_registration_path, class: 'btn btn-default' - - if @member == current_member && !@member.paid? - = link_to "Upgrade account", shop_path, class: 'btn btn-default' - if can?(:create, Notification) && current_member != @member = link_to 'Send message', new_notification_path(recipient_id: @member.id), class: 'btn btn-default' @@ -33,7 +31,7 @@ = render partial: "gardens", locals: { member: @member, gardens: @gardens } .col-md-3 = render partial: "avatar", locals: { member: @member } - = render partial: "account", locals: { member: @member } + = render partial: "roles", locals: { member: @member } = render partial: "stats", locals: { member: @member } = render partial: "contact", locals: { member: @member, twitter_auth: @twitter_auth, diff --git a/app/views/orders/complete.html.haml b/app/views/orders/complete.html.haml deleted file mode 100644 index e2f4b97ad..000000000 --- a/app/views/orders/complete.html.haml +++ /dev/null @@ -1,45 +0,0 @@ -- content_for :title, "Completed order" - -%p Thank you for your order. - -%p - %strong Completed at: - = @order.completed_at - -%p - %strong Order number: - = @order.id - -= render "shared/account_status" - -%h2 Order items - -%table.table.table-striped - %tr - %th Product - %th Price - %th Quantity - %th Subtotal - - total = 0 - - @order.order_items.each do |i| - %tr - %td= i.product.name - %td - = price_with_currency(i.price) - %td= i.quantity - %td - - subtotal = i.price * i.quantity - - total += subtotal - = price_with_currency(subtotal) - - %tr - %td - %td - %td - %strong Total: - %td - %strong - = price_with_currency(total) - -%p - = link_to "View other orders/order history", orders_path diff --git a/app/views/orders/index.html.haml b/app/views/orders/index.html.haml deleted file mode 100644 index b4741c44e..000000000 --- a/app/views/orders/index.html.haml +++ /dev/null @@ -1,44 +0,0 @@ -- content_for :title, "Your Account" - -= render "shared/account_status" - - -%h2 Orders - - -- if current_member.orders.present? - - %p - Your order history shows what you have bought via our - = succeed "." do - = link_to "shop", shop_path - - %table.table.table-striped - %tr - %th Order number - %th Date completed - %th Items - %th - - @orders.each do |order| - %tr - %td= order.id - %td - - if order.completed_at - = order.completed_at.to_s - - else - In progress - %td - - unless order.order_items.empty? - - order.order_items.each do |o| - = o.quantity - x - = o.product.name - @ - = price_with_currency(o.price) - %br/ - %td= link_to 'Details', order, class: 'btn btn-default btn-xs' -- else - %p - You have not made any orders. You can place an order via our - = succeed "." do - = link_to "shop", shop_path diff --git a/app/views/orders/show.html.haml b/app/views/orders/show.html.haml deleted file mode 100644 index 0998407f9..000000000 --- a/app/views/orders/show.html.haml +++ /dev/null @@ -1,84 +0,0 @@ -- content_for :title, @order.completed_at ? "Order details (##{@order.id})" : "Current order" - -%p - %strong Order number: - = @order.id - -%p - %strong Ordered by: - = link_to @order.member, @order.member - -%p - %strong Date begun: - = @order.created_at.to_s - -- if @order.completed_at - %p - %strong Date completed: - = @order.completed_at.to_s - -- if @order.referral_code - %p - %strong Referral code: - = @order.referral_code - -- if current_member.role? :admin - %p - %strong Paypal Express token: - = @order.paypal_express_token - %p - %strong Paypal Express payer ID: - = @order.paypal_express_payer_id - -%h2 Order items - -%table.table.table-striped - %tr - %th Product - %th Price - %th Quantity - %th Subtotal - - @order.order_items.each do |i| - %tr - %td= i.product.name - %td - = price_with_currency(i.price) - %td= i.quantity - %td - - subtotal = i.price * i.quantity - = price_with_currency(subtotal) - - %tr - %td - %td - %td - %strong Total: - %td - %strong - = price_with_currency(@order.total) - = forex_link(@order.total) - -- if @order.errors.any? - .alert - #error_explanation - %h3 - = pluralize(@order.errors.size, "error") - stopped you from checking out: - %ul - - @order.errors.full_messages.each do |msg| - %li= msg - -- if can?(:complete, @order) || can?(:destroy, @order) - = form_tag(checkout_order_path(@order), method: :get, class: 'form-inline') do - %p - - if can? :complete, @order - = label_tag :referral_code, "Do you have a referral code?" - = text_field_tag :referral_code, @order.referral_code, class: 'input-medium' - = submit_tag "Checkout with PayPal", class: 'btn btn-primary' - - if can? :destroy, @order - = link_to 'Delete this order', @order, method: :delete, - data: { confirm: 'Are you sure?' }, - class: 'btn btn-default' - = link_to "View other orders/order history", orders_path, class: 'btn btn-default' - -%p diff --git a/app/views/photos/_actions.html.haml b/app/views/photos/_actions.html.haml new file mode 100644 index 000000000..878d985ef --- /dev/null +++ b/app/views/photos/_actions.html.haml @@ -0,0 +1,6 @@ +- if can?(:edit, @photo) && can?(:destroy, @photo) + %p.photo-actions + - if can?(:edit, @photo) + = render 'shared/buttons/edit', path: edit_photo_path(@photo) + - if can?(:destroy, @photo) + = render 'shared/buttons/delete', path: photo_path(@photo) diff --git a/app/views/photos/_item_photos.haml b/app/views/photos/_item_photos.haml index 679871680..f0642c336 100644 --- a/app/views/photos/_item_photos.haml +++ b/app/views/photos/_item_photos.haml @@ -1,16 +1,14 @@ +%h2 Photos + - if photos.size.positive? || (can?(:edit, item) && can?(:create, Photo)) - %h2 Photos - if photos.size.positive? + = page_entries_info photos + = will_paginate photos .row - .pagination - = page_entries_info photos - = will_paginate photos - .row - - photos.each do |photo| - .col-md-2.six-across= render 'photos/thumbnail', photo: photo - - if can?(:create, Photo) && can?(:edit, item) - .col-md-2 - .thumbnail - = link_to new_photo_path(type: type, id: item.id), class: 'btn btn-primary' do - %span.glyphicon.glyphicon-camera{ title: "Add photo" } - Add photo + - photos.each do |photo| + .col-xs-6.col-md-3.six-across= render 'photos/thumbnail', photo: photo + +- if can?(:create, Photo) && can?(:edit, item) + = link_to new_photo_path(type: type, id: item.id), class: 'btn btn-primary' do + %span.glyphicon.glyphicon-camera{ title: "Add photo" } + Add photo diff --git a/app/views/photos/_thumbnail.html.haml b/app/views/photos/_thumbnail.html.haml index 03a1f0c4c..3be4a26d6 100644 --- a/app/views/photos/_thumbnail.html.haml +++ b/app/views/photos/_thumbnail.html.haml @@ -1,10 +1,12 @@ -.thumbnail.photo-thumbnail - = link_to image_tag(photo.thumbnail_url, alt: photo.title, class: 'img img-responsive'), photo - .text - %p - = link_to photo.title, photo - %br/ - %small - %i - by - = link_to photo.owner, photo.owner +.thumbnail + .photo-thumbnail + = link_to image_tag(photo.thumbnail_url, alt: photo.title, class: 'img img-responsive'), photo + .text + %p + = link_to photo.title, photo + %br/ + %small + %i + by + = link_to photo.owner, photo.owner + = I18n.l(photo.created_at.to_date) diff --git a/app/views/photos/show.html.haml b/app/views/photos/show.html.haml index 7e52a11e9..3758883b6 100644 --- a/app/views/photos/show.html.haml +++ b/app/views/photos/show.html.haml @@ -12,15 +12,7 @@ %p= image_tag(@photo.fullsize_url, alt: @photo.title, class: 'img img-responsive') .col-md-4 - %p - - if can? :destroy, @photo - = link_to @photo, method: :delete, - data: { confirm: 'Are you sure?' }, class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-trash{ title: "Delete" } - - - if can? :edit, @photo - = link_to edit_photo_path(@photo), class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-pencil{ title: "Edit" } + = render 'photos/actions', photo: @photo %p %strong Posted by: = link_to @photo.owner, @photo.owner diff --git a/app/views/plantings/_actions.html.haml b/app/views/plantings/_actions.html.haml index b2c2d5f98..87eddc8cf 100644 --- a/app/views/plantings/_actions.html.haml +++ b/app/views/plantings/_actions.html.haml @@ -1,27 +1,12 @@ -- if can?(:edit, planting) || can?(:destroy, planting) - - if can? :edit, planting - = link_to edit_planting_path(planting), class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-pencil{ title: "Edit" } - Edit +- if can?(:edit, planting) + .btn-group.planting-actions + = render 'shared/buttons/edit', path: edit_planting_path(planting) + = render 'shared/buttons/add_photo', path: new_photo_path(id: planting.id, type: 'planting') + + - if planting.active? + = render 'shared/buttons/finish_planting', planting: planting + = render 'shared/buttons/harvest_planting', planting: planting + = render 'shared/buttons/save_seeds', planting: planting + - if can? :destroy, planting - = link_to planting, method: :delete, - data: { confirm: 'Are you sure?' }, class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-trash{ title: "Delete" } - Delete - - - unless planting.finished - = link_to planting_path(planting, planting: { finished: 1 }), - method: :put, class: 'btn btn-default btn-xs append-date' do - - %span.glyphicon.glyphicon-ok{ title: "Finished" } - Mark as finished - - - if can? :edit, planting - = link_to new_planting_harvest_path(planting), class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-leaf{ title: "Harvest" } - Harvest - - - if can?(:edit, planting) && can?(:create, Photo) - = link_to new_photo_path(id: planting.id, type: 'planting'), class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-camera{ title: "Add photo" } - Add photo + = render 'shared/buttons/delete', path: planting diff --git a/app/views/plantings/_card.html.haml b/app/views/plantings/_card.html.haml index a5469461a..0f9518e6c 100644 --- a/app/views/plantings/_card.html.haml +++ b/app/views/plantings/_card.html.haml @@ -8,7 +8,7 @@ .panel-body .row .col-xs-12.col-md-5 - = link_to image_tag((planting.default_photo ? planting.default_photo.thumbnail_url : 'placeholder_150.png'), + = link_to image_tag(planting_image_path(planting), alt: planting.crop_id, class: 'img img-responsive'), planting .col-xs-12.col-md-7 @@ -35,24 +35,6 @@ %dt Finish expected: %dd= planting.finish_predicted_at if planting.finish_predicted_at.present? - %p= render 'plantings/progress', planting: planting, show_explanation: true - - = link_to 'Details', planting_path(planting), - class: 'btn btn-default btn-xs' - - - if can?(:edit, planting) && can?(:create, Harvest) - = link_to 'Harvest', new_planting_harvest_path(planting), - class: 'btn btn-default btn-xs' - - - if can?(:edit, planting) && !planting.finished - = link_to "Mark as finished", - planting_path(planting, planting: { finished: 1 }), - method: :put, - class: 'btn btn-default btn-xs append-date' - - - if can? :destroy, planting - = link_to planting, method: :delete, - data: { confirm: 'Are you sure?' }, - class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-trash{ title: "Delete" } + .row + .col-md-12= render 'plantings/actions', planting: planting diff --git a/app/views/plantings/_descendants.html.haml b/app/views/plantings/_descendants.html.haml new file mode 100644 index 000000000..27e81c6b8 --- /dev/null +++ b/app/views/plantings/_descendants.html.haml @@ -0,0 +1,14 @@ +%h2 Seeds saved from this planting + +- if planting.child_seeds.size.positive? + .row + - planting.child_seeds.each do |seed| + .col-xs-6.col-sm-4.col-md-3 + = render 'seeds/thumbnail', seed: seed +- else + %p No seeds saved + +- if planting.active? && can?(:create, Seed) && can?(:edit, planting) + = link_to new_planting_seed_path(planting), class: 'btn btn-primary' do + %span.glyphicon.glyphicon-heart{ title: "Add photo" } + Save seeds diff --git a/app/views/plantings/_form.html.haml b/app/views/plantings/_form.html.haml index de2e55c30..874b5329b 100644 --- a/app/views/plantings/_form.html.haml +++ b/app/views/plantings/_form.html.haml @@ -13,15 +13,19 @@ .form-group.required = f.label :crop, 'What did you plant?', class: 'control-label col-md-2' .col-md-8 - = auto_suggest @planting, :crop, class: 'form-control', default: @crop - %span.help-inline - Can't find what you're looking for? - = link_to "Request new crops.", new_crop_path + - if @seed.present? + = link_to @seed, seed_path(@seed) + = f.hidden_field :parent_seed_id, value: @seed.id + - else + = auto_suggest @planting, :crop, class: 'form-control', default: @crop + %span.help-inline + Can't find what you're looking for? + = link_to "Request new crops.", new_crop_path .form-group.required = f.label :garden_id, 'Where did you plant it?', class: 'control-label col-md-2' .col-md-8 = collection_select(:planting, :garden_id, - Garden.active.where(owner_id: current_member), + current_member.gardens.active.order("lower(gardens.name)"), :id, :name, selected: @planting.garden_id || @garden.id, class: 'form-control') diff --git a/app/views/plantings/_harvests.html.haml b/app/views/plantings/_harvests.html.haml index 5edf14a3d..a845e980e 100644 --- a/app/views/plantings/_harvests.html.haml +++ b/app/views/plantings/_harvests.html.haml @@ -1,12 +1,13 @@ +%h2 Harvests - if planting.harvests.empty? - %p no harvests yet + %p No harvests - else .row - planting.harvests.order(created_at: :desc).includes(:crop).each do |harvest| - .col-xs-6.col-md-2 + .col-xs-6.col-sm-4.col-md-3 = render 'harvests/thumbnail', harvest: harvest - - if can? :edit, planting - .col-xs-6.col-md-2 - = link_to new_planting_harvest_path(planting), class: 'btn btn-primary' do - %span.glyphicon.glyphicon-leaf{ title: "Harvest" } - Harvest + +- if planting.active? && can?(:edit, planting) + = link_to new_planting_harvest_path(planting), class: 'btn btn-primary' do + %span.glyphicon.glyphicon-leaf{ title: "Harvest" } + Harvest diff --git a/app/views/plantings/_image_with_popover.html.haml b/app/views/plantings/_image_with_popover.html.haml index d958a9d41..70bfc1836 100644 --- a/app/views/plantings/_image_with_popover.html.haml +++ b/app/views/plantings/_image_with_popover.html.haml @@ -1,10 +1,10 @@ - cache planting do - = link_to image_tag(planting.photos.present? ? planting.photos.first.thumbnail_url : 'placeholder_150.png', + = link_to image_tag(planting_image_path(planting), alt: planting.to_s, class: 'image-responsive crop-image'), planting, rel: "popover", 'data-trigger': 'hover', 'data-title': planting.to_s, - 'data-content': render(partial: 'plantings/popover', locals: { planting: planting }), + 'data-content': render('plantings/popover', planting: planting), 'data-html': true diff --git a/app/views/plantings/_list.html.haml b/app/views/plantings/_list.html.haml index cf678c8a6..aa7dcf9fa 100644 --- a/app/views/plantings/_list.html.haml +++ b/app/views/plantings/_list.html.haml @@ -1,9 +1,9 @@ - plantings.each do |p| - cache p do .row - .col-md-3{ style: 'padding-bottom: 6px' } - = render partial: 'plantings/image_with_popover', locals: { planting: p } - .col-md-9 + .col-md-3.col-xs-4.homepage-listing + = render 'plantings/image_with_popover', planting: p + .col-md-9.col-xs-4 = link_to p.crop, p.crop in = succeed "'s" do @@ -12,4 +12,4 @@ %br/ %small %i - = p.owner.location + = p.location diff --git a/app/views/plantings/_thumbnail.html.haml b/app/views/plantings/_thumbnail.html.haml index 15ff2553e..e34c653da 100644 --- a/app/views/plantings/_thumbnail.html.haml +++ b/app/views/plantings/_thumbnail.html.haml @@ -1,7 +1,7 @@ .thumbnail .planting-thumbnail - if planting - = link_to image_tag((planting.default_photo ? planting.default_photo.thumbnail_url : 'placeholder_150.png'), + = link_to image_tag(planting_image_path(planting), alt: planting.crop.name, class: 'img'), planting .plantinginfo diff --git a/app/views/plantings/show.html.haml b/app/views/plantings/show.html.haml index 88789ed5a..49be10fa5 100644 --- a/app/views/plantings/show.html.haml +++ b/app/views/plantings/show.html.haml @@ -9,9 +9,11 @@ = tag("meta", property: "og:url", content: request.original_url) = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) -%p= render 'plantings/actions', planting: @planting +- content_for :buttonbar do + = render 'plantings/actions', planting: @planting + .row.planting - .col-md-6 + .col-xs-12.col-sm-6 %dl.dl-horizontal.planting-attributes %dt Owner: %dd @@ -26,14 +28,17 @@ %dd = link_to "#{@planting.owner}'s", @planting.owner = link_to @planting.garden, @planting.garden - - unless @planting.owner.location.blank? + - if @planting.owner.location.present? (#{@planting.owner.location}) %dt Quantity: %dd= display_planting_quantity(@planting) - - unless @planting.planted_from.blank? - %dt Planted from: - %dd= display_planted_from(@planting) + %dt Planted from: + %dd + - if @planting.parent_seed + = link_to @planting.parent_seed, seed_path(@planting.parent_seed) + - else + = display_planted_from(@planting) %dt Sun or shade? %dd @@ -64,23 +69,23 @@ %dt Last Harvest: %dd #{@planting.days_to_last_harvest} days after planting + %dt Progress + %dd= render 'plantings/progress', planting: @planting, show_explanation: true - %h2 Progress - %p= render 'plantings/progress', planting: @planting, show_explanation: true - %h2 Harvests - %p= render 'plantings/harvests', planting: @planting - - .col-md-6 + .col-xs-12.col-sm-6 = render partial: "crops/index_card", locals: { crop: @planting.crop } - if @planting.owner.location %p %small View other plantings, members and more near = link_to @planting.owner.location, place_path(@planting.owner.location, anchor: "plantings") -- if @planting.description - %h2 Notes - - :growstuff_markdown - #{ @planting.description != "" ? strip_tags(@planting.description) : "No description given." } - -= render 'photos/item_photos', item: @planting, type: 'planting', photos: @photos +.row + .col-md-6 + - if @planting.description + %h2 Notes + :growstuff_markdown + #{ @planting.description != "" ? strip_tags(@planting.description) : "No description given." } + = render 'plantings/harvests', planting: @planting + = render 'plantings/descendants', planting: @planting + .col-md-6 + = render 'photos/item_photos', item: @planting, type: 'planting', photos: @photos diff --git a/app/views/products/_form.html.haml b/app/views/products/_form.html.haml deleted file mode 100644 index a3aa8a2f0..000000000 --- a/app/views/products/_form.html.haml +++ /dev/null @@ -1,32 +0,0 @@ -= form_for @product do |f| - - if @product.errors.any? - #error_explanation - %h2 - = pluralize(@product.errors.size, "error") - prohibited this product from being saved: - %ul - - @product.errors.full_messages.each do |msg| - %li= msg - - .field - = f.label :name - = f.text_field :name, class: 'form-control' - .field - = f.label :description - = f.text_area :description, class: 'form-control' - .field - = f.label :min_price, "Minimum price (in cents)" - = f.text_field :min_price - .field - = f.label :recommended_price, "Recommended price (in cents)" - = f.text_field :recommended_price - .field - = f.label :account_type - = collection_select(:product, :account_type_id, AccountType.all, :id, - :name, selected: @product.account_type_id) - .field - = f.label :paid_months - = f.text_field :paid_months - .form-group - .actions - = f.submit 'Save' diff --git a/app/views/products/edit.html.haml b/app/views/products/edit.html.haml deleted file mode 100644 index 84ebcb870..000000000 --- a/app/views/products/edit.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- content_for :title, "Editing product" - -= render 'form' - -= link_to 'Show', @product -\| -= link_to 'Back', products_path diff --git a/app/views/products/index.html.haml b/app/views/products/index.html.haml deleted file mode 100644 index 4945d130e..000000000 --- a/app/views/products/index.html.haml +++ /dev/null @@ -1,29 +0,0 @@ -- content_for :title, "Listing products" - -%table - %tr - %th Name - %th Description - %th Min price - %th Recommended price - %th Account type - %th Paid months - %th - %th - %th - - - @products.each do |product| - %tr - %td= product.name - %td= product.description - %td= product.min_price - %td= product.recommended_price - %td= product.account_type ? product.account_type.name : "" - %td= product.paid_months - %td= link_to 'Show', product - %td= link_to 'Edit', edit_product_path(product) - %td= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } - -%br - -= link_to 'New Product', new_product_path diff --git a/app/views/products/new.html.haml b/app/views/products/new.html.haml deleted file mode 100644 index 57b944d9a..000000000 --- a/app/views/products/new.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -- content_for :title, "New product" - -= render 'form' - -= link_to 'Back', products_path diff --git a/app/views/products/show.html.haml b/app/views/products/show.html.haml deleted file mode 100644 index 61b35519f..000000000 --- a/app/views/products/show.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -%p#notice= notice - -%p - %b Name: - = @product.name -%p - %b Description: - :growstuff_markdown - #{strip_tags(@product.description)} -%p - %b Min price: - = @product.min_price -%p - %b Recommended price: - = @product.recommended_price -%p - %b Account type: - = @product.account_type.name -%p - %b Paid months: - = @product.paid_months - -= link_to 'Edit', edit_product_path(@product) -\| -= link_to 'Back', products_path diff --git a/app/views/seeds/_actions.html.haml b/app/views/seeds/_actions.html.haml index 9584182f0..803b63b2d 100644 --- a/app/views/seeds/_actions.html.haml +++ b/app/views/seeds/_actions.html.haml @@ -1,11 +1,15 @@ -- if can? :edit, @seed - = link_to edit_seed_path(@seed), class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-pencil{ title: "Edit" } - Edit - = link_to new_photo_path(id: seed.id, type: 'seed'), class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-camera{ title: "Add photo" } - Add photo -- if can? :destroy, @seed - = link_to @seed, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-trash{ title: "Delete" } - Delete + +- if can? :edit, seed + .btn-group + = render 'shared/buttons/edit', path: edit_seed_path(seed) + = render 'shared/buttons/add_photo', path: new_photo_path(id: seed.id, type: 'seed') + + - if can?(:create, Planting) && seed.active? + = link_to new_planting_path(seed_id: seed), class: 'btn btn-default btn-xs' do + %span.glyphicon.glyphicon-grain{ title: "Plant seeds" } + Plant seeds + + = render 'shared/buttons/finish_seeds', seed: seed + +- if can? :destroy, seed + = render 'shared/buttons/delete', path: seed diff --git a/app/views/seeds/_card.html.haml b/app/views/seeds/_card.html.haml new file mode 100644 index 000000000..8cb70f64f --- /dev/null +++ b/app/views/seeds/_card.html.haml @@ -0,0 +1,32 @@ +.panel.panel-success + .panel-heading + %h3.panel-title + = link_to seed, seed + - if can? :edit, seed + %a.pull-right{ href: edit_seed_path(seed), role: "button", id: "edit_seed_glyphicon" } + %span.glyphicon.glyphicon-pencil{ title: "Edit" } + .panel-body + .row + .col-md-4 + = link_to image_tag(seed_image_path(seed), + alt: seed.crop.name, class: 'img'), + seed.crop + .col-md-8 + %dl.dl-horizontal + %dt Crop : + %dd= link_to seed.crop.name, seed.crop + - if seed.parent_planting.present? + %dt Saved from + %dd= link_to seed.parent_planting, planting_path(seed.parent_planting) + %dt Plant before : + %dd= seed.plant_before + %dt Quantity : + %dd= seed.quantity + %dt Will trade to : + %dd= seed.tradable_to + %dt From location : + %dd= seed.owner.location + %dt Owner : + %dd= link_to seed.owner.login_name, seed.owner + .col-md-12 + %p= render 'seeds/actions', seed: seed diff --git a/app/views/seeds/_descendants.html.haml b/app/views/seeds/_descendants.html.haml new file mode 100644 index 000000000..1736d0bca --- /dev/null +++ b/app/views/seeds/_descendants.html.haml @@ -0,0 +1,13 @@ +%h2 Plants grown from these seeds +- if @seed.child_plantings + .row + - seed.child_plantings.each do |planting| + .col-md-3 + = render 'plantings/thumbnail', planting: planting +- else + %p No plants grown yet. + +- if can?(:create, Planting) && can?(:edit, seed) + = link_to new_seed_planting_path(seed), class: 'btn btn-primary' do + %span.glyphicon.glyphicon-grain{ title: "Plant seeds" } + Plant seeds diff --git a/app/views/seeds/_form.html.haml b/app/views/seeds/_form.html.haml index b7cb3ea36..9bd2dd280 100644 --- a/app/views/seeds/_form.html.haml +++ b/app/views/seeds/_form.html.haml @@ -9,14 +9,17 @@ %ul - @seed.errors.full_messages.each do |msg| %li= msg - .form-group.required = f.label :crop, 'Crop:', class: 'control-label col-md-2' .col-md-8 - = auto_suggest @seed, :crop, class: 'form-control', default: @crop - %span.help-inline - Can't find what you're looking for? - = link_to "Request new crops.", new_crop_path + - if @planting + = link_to @planting, planting_path(@planting) + = f.hidden_field :parent_planting_id, value: @planting.id + - else + = auto_suggest @seed, :crop, class: 'form-control', default: @crop + %span.help-inline + Can't find what you're looking for? + = link_to "Request new crops.", new_crop_path .form-group = f.label :quantity, 'Quantity:', class: 'control-label col-md-2' .col-md-2 @@ -28,6 +31,19 @@ = f.text_field :plant_before, class: 'add-datepicker form-control', value: @seed.plant_before ? @seed.plant_before.to_s(:ymd) : '' = render partial: 'shared/form_optional' + .form-group + = f.label :finished, 'Mark as finished', class: 'control-label col-md-2' + .col-md-8 + = f.check_box :finished + = render partial: 'shared/form_optional' + %span.help-block + = t('.finish_helper') + .form-group + = f.label :finished_at, 'Finished at:', class: 'control-label col-md-2' + .col-md-2 + = f.text_field :finished_at, class: 'add-datepicker form-control', + value: @seed.finished_at ? @seed.finished_at.to_s(:ymd) : '' + = render partial: 'shared/form_optional' .form-group = f.label :days_until_maturity_min, 'Days until maturity:', class: 'control-label col-md-2' %fieldset diff --git a/app/views/seeds/_thumbnail.html.haml b/app/views/seeds/_thumbnail.html.haml index edd464e57..38aa66d65 100644 --- a/app/views/seeds/_thumbnail.html.haml +++ b/app/views/seeds/_thumbnail.html.haml @@ -1,31 +1,14 @@ -.panel.panel-success - .panel-heading - %h3.panel-title - = link_to "#{seed.owner.login_name}'s seed", seed - - if can? :edit, seed - %a.pull-right{ href: edit_seed_path(seed), role: "button", id: "edit_seed_glyphicon" } - %span.glyphicon.glyphicon-pencil{ title: "Edit" } - .panel-body - .row - .col-md-4 - = link_to image_tag((seed.crop.default_photo ? seed.crop.default_photo.thumbnail_url : 'placeholder_150.png'), - alt: seed.crop.name, class: 'img'), - seed.crop - .col-md-8 - %dl.dl-horizontal - %dt Crop : - %dd= link_to seed.crop.name, seed.crop - %dt Plant before : - %dd= seed.plant_before - %dt Quantity : - %dd= seed.quantity - %dt Will trade to : - %dd= seed.tradable_to - %dt From location : - %dd= seed.owner.location - %dt Owner : - %dd= link_to seed.owner.login_name, seed.owner - .panel-footer - %dt Description - %dd - = display_seed_description(seed) +.thumbnail + .seed-thumbnail + = link_to image_tag(seed_image_path(seed), + alt: seed.crop.name, class: 'img'), + seed_path(seed) + .seedinfo + .seed-name + = link_to seed, seed_path(seed) + .trade-to + %p= seed.owner.location + %p + Will trade to: + %br/ + = seed.tradable_to diff --git a/app/views/seeds/index.html.haml b/app/views/seeds/index.html.haml index 3108bfc56..e7e1ec3e3 100644 --- a/app/views/seeds/index.html.haml +++ b/app/views/seeds/index.html.haml @@ -28,7 +28,7 @@ - unless @seeds.empty? - @seeds.each do |seed| .col-md-6 - = render partial: 'seeds/thumbnail', locals: { seed: seed } + = render 'seeds/card', seed: seed .pagination = page_entries_info @seeds diff --git a/app/views/seeds/show.html.haml b/app/views/seeds/show.html.haml index e15ffb46d..681a21324 100644 --- a/app/views/seeds/show.html.haml +++ b/app/views/seeds/show.html.haml @@ -9,47 +9,56 @@ = tag("meta", property: "og:url", content: request.original_url) = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) +- content_for :buttonbar do + = render 'seeds/actions', seed: @seed + .row .col-md-6 - %p - %b Owner: - = link_to @seed.owner, @seed.owner - — - = link_to "view all #{@seed.owner}'s seeds", seeds_by_owner_path(owner: @seed.owner.slug) - %p - %b Quantity: - = @seed.quantity.blank? ? "not specified" : @seed.quantity - %p - %b Plant before: - = @seed.plant_before.to_s - %p - %b Days until maturity: - = render partial: 'days_until_maturity', locals: { seed: @seed } - %p - %b Organic? - = @seed.organic - %p - %b GMO? - = @seed.gmo - %p - %b Heirloom? - = @seed.heirloom - %p - %b Will trade: - = @seed.tradable_to - - if @seed.owner.location.blank? - (from unspecified location) - - if current_member == @seed.owner - = link_to "Set Location", edit_registration_path(current_member), class: 'btn btn-default btn-xs' - - else - (from - = succeed ")" do - = link_to @seed.owner.location, place_path(@seed.owner.location, anchor: "seeds") + %dl.dl-horizontal + %dt Owner + %dd + = link_to @seed.owner, @seed.owner + — + = link_to "view all #{@seed.owner}'s seeds", + seeds_by_owner_path(owner: @seed.owner.slug) + %dt Quantity: + %dd= @seed.quantity.blank? ? "not specified" : @seed.quantity + %dt Plant before: + %dd= @seed.plant_before.to_s + -if @seed.finished_at + %dt Finished at: + %dd= @seed.finished_at.to_s + %dt Days until maturity: + %dd= render partial: 'days_until_maturity', locals: { seed: @seed } + %dt Organic? + %dd= @seed.organic + %dt GMO? + %dd= @seed.gmo + %dt Heirloom? + %dd= @seed.heirloom + %dt Will trade: + %dd + = @seed.tradable_to + - if @seed.owner.location.blank? + (from unspecified location) + - if current_member == @seed.owner + = link_to "Set Location", edit_registration_path(current_member), class: 'btn btn-default btn-xs' + - else + (from + = succeed ")" do + = link_to @seed.owner.location, place_path(@seed.owner.location, anchor: "seeds") - %p - %b Description: - :growstuff_markdown - #{ @seed.description != "" ? strip_tags(@seed.description) : "No description given." } + %dt When? + %dd + = @seed.created_at + - if @seed.parent_planting + %dt Saved from planting: + %dd + = link_to @seed.parent_planting, planting_path(@seed.parent_planting) + %dt Description: + %dd + :growstuff_markdown + #{ @seed.description != "" ? strip_tags(@seed.description) : "No description given." } - if current_member - if @seed.tradable? && current_member != @seed.owner @@ -60,9 +69,9 @@ - else = render 'shared/signin_signup', to: 'request seeds' - - if can?(:edit, @seed) || can?(:destroy, @seed) - %p - = render 'actions', seed: @seed + = render 'seeds/descendants', seed: @seed + = render 'photos/item_photos', item: @seed, type: 'seed', photos: @photos + .col-md-6 = render partial: "crops/index_card", locals: { crop: @seed.crop } - if @seed.owner.location @@ -75,5 +84,3 @@ Or = link_to "purchase seeds via Ebay", crop_ebay_seeds_url(@seed.crop), target: "_blank", rel: "noopener noreferrer" - -= render 'photos/item_photos', item: @seed, type: 'seed', photos: @photos diff --git a/app/views/shared/_account_status.html.haml b/app/views/shared/_account_status.html.haml deleted file mode 100644 index a39870956..000000000 --- a/app/views/shared/_account_status.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -%h2 Your current account status - -%p - %strong Account type: - = current_member.account_type.name - -- if current_member.account.paid_until_string - %p - %strong Paid until: - = current_member.account.paid_until_string - -- unless current_member.paid? - = link_to "Upgrade and support #{ENV['GROWSTUFF_SITE_NAME']}", shop_path, class: 'btn btn-primary' diff --git a/app/views/shared/buttons/_add_photo.haml b/app/views/shared/buttons/_add_photo.haml new file mode 100644 index 000000000..71e97c0e9 --- /dev/null +++ b/app/views/shared/buttons/_add_photo.haml @@ -0,0 +1,4 @@ +- if can?(:create, Photo) + = link_to path, class: 'btn btn-default btn-xs' do + %span.glyphicon.glyphicon-camera{ title: "Add photo" } + Add photo diff --git a/app/views/shared/buttons/_delete.haml b/app/views/shared/buttons/_delete.haml new file mode 100644 index 000000000..b7490837b --- /dev/null +++ b/app/views/shared/buttons/_delete.haml @@ -0,0 +1,4 @@ += link_to path, method: :delete, + data: { confirm: 'Are you sure?' }, class: 'btn btn-default btn-xs' do + %span.glyphicon.glyphicon-trash{ title: "Delete" } + Delete diff --git a/app/views/shared/buttons/_edit.haml b/app/views/shared/buttons/_edit.haml new file mode 100644 index 000000000..87d464023 --- /dev/null +++ b/app/views/shared/buttons/_edit.haml @@ -0,0 +1,3 @@ += link_to path, class: 'btn btn-default btn-xs' do + %span.glyphicon.glyphicon-pencil{ title: "Edit" } + Edit diff --git a/app/views/shared/buttons/_finish_planting.html.haml b/app/views/shared/buttons/_finish_planting.html.haml new file mode 100644 index 000000000..9a3b6ce75 --- /dev/null +++ b/app/views/shared/buttons/_finish_planting.html.haml @@ -0,0 +1,5 @@ +- unless planting.finished + = link_to planting_path(planting, planting: { finished: 1 }), + method: :put, class: 'btn btn-default btn-xs append-date' do + %span.glyphicon.glyphicon-ok{ title: "Finished" } + Mark as finished diff --git a/app/views/shared/buttons/_finish_seeds.haml b/app/views/shared/buttons/_finish_seeds.haml new file mode 100644 index 000000000..211e3ec8b --- /dev/null +++ b/app/views/shared/buttons/_finish_seeds.haml @@ -0,0 +1,5 @@ +- unless seed.finished + = link_to seed_path(seed, seed: { finished: 1 }), + method: :put, class: 'btn btn-default btn-xs append-date' do + %span.glyphicon.glyphicon-ok{ title: "Finished" } + Mark as finished diff --git a/app/views/shared/buttons/_harvest_planting.haml b/app/views/shared/buttons/_harvest_planting.haml new file mode 100644 index 000000000..c90e163ad --- /dev/null +++ b/app/views/shared/buttons/_harvest_planting.haml @@ -0,0 +1,4 @@ +- planting.active? && if can?(:create, Harvest) && can?(:edit, planting) + = link_to new_planting_harvest_path(planting), class: 'btn btn-default btn-xs' do + %span.glyphicon.glyphicon-leaf{ title: "Harvest" } + Harvest diff --git a/app/views/shared/buttons/_save_seeds.haml b/app/views/shared/buttons/_save_seeds.haml new file mode 100644 index 000000000..00d5febb0 --- /dev/null +++ b/app/views/shared/buttons/_save_seeds.haml @@ -0,0 +1,4 @@ +- if planting.active? + = link_to new_planting_seed_path(planting), class: 'btn btn-default btn-xs' do + %span.glyphicon.glyphicon-heart{ title: "Save seeds" } + Save seeds diff --git a/app/views/shop/index.html.haml b/app/views/shop/index.html.haml deleted file mode 100644 index 6266050f0..000000000 --- a/app/views/shop/index.html.haml +++ /dev/null @@ -1,83 +0,0 @@ -- content_for :title, t('.title') - -%p - Growstuff relies on your support to build and run this open source - platform for food growers. We do not have outside investment, and do - not accept ads. Instead, we offer paid memberships, which give you - access to premium features, and ensure that we focus our efforts on - keeping you, our members, happy. - -%p - We are currently developing a number of advanced features for paid - members. We will announce our progress on these in our - = link_to "Feedback and Support forum", "http://www.growstuff.org/forums/growstuff-feedback-support" - as well as via other channels. - -%p - All our accounts are priced on a sliding scale. You can choose how - much you want to pay. Remember, your subscription supports an open - source, open data platform supporting home food growers and promoting - sustainable food systems! - -- if current_member && current_member.paid? - %h2 Thank you for supporting Growstuff - - %p You currently have a paid membership, and can't buy another one at this time. - - = render "shared/account_status" - -- elsif @order && !@order.order_items.empty? - %h2 Your current order - - %p - You currently have the following item in your cart: - %strong - = @most_recent_item.product - @ - = price_with_currency(@most_recent_item.price) - - %p - = link_to "View order and checkout", order_path(@order) - or - = succeed "." do - = link_to 'delete this order', @order, method: :delete, - data: { confirm: 'Are you sure?' } - -- else - - @products.each do |p| - %h2= p.name - - %div - :growstuff_markdown - #{ strip_tags p.description } - - %p - Pay what you want, starting at - = succeed "." do - = price_with_currency(p.min_price) - = forex_link(p.min_price) - - if p.recommended_price - Recommended price: - = succeed "." do - = price_with_currency(p.recommended_price) - = forex_link(p.recommended_price) - - %div - - if can? :create, Order - - = form_for @order_item do |f| - .field - = f.text_field :price, value: price_in_dollars(p.recommended_price || p.min_price) - .field - = f.hidden_field :product_id, value: p.id - .field - = f.hidden_field :quantity, value: 1 - .actions - = f.submit 'Buy', class: 'btn btn-primary' - - - else - Please - = link_to "sign in", new_member_session_path - or - = link_to "sign up", new_member_registration_path - to purchase. diff --git a/config/application.rb b/config/application.rb index 3a07ca429..0a00a917a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -44,9 +44,6 @@ module Growstuff # Configure the default encoding used in templates for Ruby 1.9. config.encoding = "utf-8" - # Configure a default account type - config.default_account_type = "Free" - # Configure sensitive parameters which will be filtered from the log file. config.filter_parameters += [:password] diff --git a/config/application.yml.example b/config/application.yml.example index 4aa23e439..ba343b6d0 100644 --- a/config/application.yml.example +++ b/config/application.yml.example @@ -57,11 +57,6 @@ GROWSTUFF_TWITTER_SECRET: "" GROWSTUFF_FLICKR_KEY: "" GROWSTUFF_FLICKR_SECRET: "" -# Paypal is used for payments, obviously. -GROWSTUFF_PAYPAL_USERNAME: "dummy" -GROWSTUFF_PAYPAL_PASSWORD: "dummy" -GROWSTUFF_PAYPAL_SIGNATURE: "dummy" - # https://developers.facebook.com/ GROWSTUFF_FACEBOOK_KEY: "" GROWSTUFF_FACEBOOK_SECRET: "" diff --git a/config/environments/development.rb b/config/environments/development.rb index 39abd19f1..363c0e32a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -72,17 +72,6 @@ Rails.application.configure do config.mapbox_map_id = 'growstuff.i3n2il6a' config.mapbox_access_token = 'pk.eyJ1IjoiZ3Jvd3N0dWZmIiwiYSI6IkdxMkx4alUifQ.n0igaBsw97s14zMa0lwKCA' - config.after_initialize do - ActiveMerchant::Billing::Base.mode = :test - paypal_options = { - login: ENV['GROWSTUFF_PAYPAL_USERNAME'] || 'dummy', - password: ENV['GROWSTUFF_PAYPAL_PASSWORD'] || 'dummy', - signature: ENV['GROWSTUFF_PAYPAL_SIGNATURE'] || 'dummy' - } - ::STANDARD_GATEWAY = ActiveMerchant::Billing::PaypalGateway.new(paypal_options) - ::EXPRESS_GATEWAY = ActiveMerchant::Billing::PaypalExpressGateway.new(paypal_options) - end - config.action_controller.action_on_unpermitted_parameters = :raise config.active_job.queue_adapter = :sidekiq diff --git a/config/environments/production.rb b/config/environments/production.rb index 99e25d577..9d377db43 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -104,17 +104,6 @@ Rails.application.configure do config.mapbox_map_id = 'growstuff.i3n2c4ie' config.mapbox_access_token = 'pk.eyJ1IjoiZ3Jvd3N0dWZmIiwiYSI6IkdxMkx4alUifQ.n0igaBsw97s14zMa0lwKCA' - config.after_initialize do - ActiveMerchant::Billing::Base.mode = :production - paypal_options = { - login: ENV['GROWSTUFF_PAYPAL_USERNAME'], - password: ENV['GROWSTUFF_PAYPAL_PASSWORD'], - signature: ENV['GROWSTUFF_PAYPAL_SIGNATURE'] - } - ::STANDARD_GATEWAY = ActiveMerchant::Billing::PaypalGateway.new(paypal_options) - ::EXPRESS_GATEWAY = ActiveMerchant::Billing::PaypalExpressGateway.new(paypal_options) - end - config.active_job.queue_adapter = :sidekiq # Do not dump schema after migrations. diff --git a/config/environments/staging.rb b/config/environments/staging.rb index ce1eeaf7c..aa2afd565 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -89,16 +89,5 @@ Rails.application.configure do config.mapbox_map_id = 'growstuff.i3n2hao7' config.mapbox_access_token = 'pk.eyJ1IjoiZ3Jvd3N0dWZmIiwiYSI6IkdxMkx4alUifQ.n0igaBsw97s14zMa0lwKCA' - config.after_initialize do - ActiveMerchant::Billing::Base.mode = :test - paypal_options = { - login: ENV['GROWSTUFF_PAYPAL_USERNAME'], - password: ENV['GROWSTUFF_PAYPAL_PASSWORD'], - signature: ENV['GROWSTUFF_PAYPAL_SIGNATURE'] - } - ::STANDARD_GATEWAY = ActiveMerchant::Billing::PaypalGateway.new(paypal_options) - ::EXPRESS_GATEWAY = ActiveMerchant::Billing::PaypalExpressGateway.new(paypal_options) - end - config.active_job.queue_adapter = :sidekiq end diff --git a/config/environments/test.rb b/config/environments/test.rb index 43a86a23a..617338127 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -48,13 +48,6 @@ Rails.application.configure do config.analytics_code = '' config.currency = 'AUD' end - - config.after_initialize do - require "active_merchant/ext/paypal_bogus_gateway" - ActiveMerchant::Billing::Base.mode = :test - ::STANDARD_GATEWAY = ActiveMerchant::Billing::PaypalBogusGateway.new - ::EXPRESS_GATEWAY = ActiveMerchant::Billing::PaypalBogusGateway.new - end end Geocoder.configure(lookup: :test) diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 4b828e80c..60484d4a4 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -12,3 +12,4 @@ Rails.application.config.assets.paths << Rails.root.join('node_modules') # application.js, application.css, and all non-JS/CSS in the app/assets # folder are already added. # Rails.application.config.assets.precompile += %w( admin.js admin.css ) +Rails.application.config.assets.precompile += %w(charts.js) diff --git a/config/locales/en.yml b/config/locales/en.yml index d2848cecd..d5d3364a6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,11 +1,5 @@ --- en: - account: - update: Account detail was successfully updated. - account_types: - created: Account type was successfully created. - deleted: Account type was successfully deleted. - updated: Account type was successfully updated. activerecord: models: comment: @@ -99,7 +93,6 @@ en: open: ad_free_linktext: ad-free api_docs_linktext: API documentation - buy_account_linktext: buying a paid account creative_commons_linktext: Creative Commons license get_involved_body_html: > We believe in collaboration, and work closely with our members and the wider food-growing community. @@ -213,9 +206,6 @@ en: default: Everyone's seeds owner_seeds: "%{owner} seeds" string: "%{crop} seeds belonging to %{owner}" - shop: - index: - title: Shop unauthorized: create: all: Please sign in or sign up to create a %{subject}. diff --git a/config/routes.rb b/config/routes.rb index af84f5a07..7ef095562 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -23,16 +23,19 @@ Rails.application.routes.draw do resources :plantings do resources :harvests + resources :seeds end get '/plantings/owner/:owner' => 'plantings#index', as: 'plantings_by_owner' get '/plantings/crop/:crop' => 'plantings#index', as: 'plantings_by_crop' resources :gardens do - get 'timeline' => 'gardens#timeline' + get 'timeline' => 'charts/gardens#timeline' end get '/gardens/owner/:owner' => 'gardens#index', as: 'gardens_by_owner' - resources :seeds + resources :seeds do + resources :plantings + end get '/seeds/owner/:owner' => 'seeds#index', as: 'seeds_by_owner' get '/seeds/crop/:crop' => 'seeds#index', as: 'seeds_by_crop' @@ -52,9 +55,9 @@ Rails.application.routes.draw do get 'crops/search' => 'crops#search', as: 'crops_search' resources :crops do get 'photos' => 'photos#index' - get 'sunniness' => 'crops#sunniness' - get 'planted_from' => 'crops#planted_from' - get 'harvested_for' => 'crops#harvested_for' + get 'sunniness' => 'charts/crops#sunniness' + get 'planted_from' => 'charts/crops#planted_from' + get 'harvested_for' => 'charts/crops#harvested_for' end resources :comments @@ -72,17 +75,6 @@ Rails.application.routes.draw do get '/places/search' => 'places#search', as: 'search_places' get '/places/:place' => 'places#show', as: 'place' - # everything for paid accounts etc - resources :account_types - resources :accounts - resources :orders - get 'orders/:id/checkout' => 'orders#checkout', as: 'checkout_order' - get 'orders/:id/complete' => 'orders#complete', as: 'complete_order' - get 'orders/:id/cancel' => 'orders#cancel', as: 'cancel_order' - - resources :order_items - resources :products - resources :likes, only: %i(create destroy) get "home/index" @@ -91,15 +83,11 @@ Rails.application.routes.draw do get 'auth/:provider/callback' => 'authentications#create' get 'members/auth/:provider/callback' => 'authentications#create' - get '/shop' => 'shop#index' - get '/shop/:action' => 'shop#:action' - - # comfy_route :cms_admin, path: '/admin/cms' + comfy_route :cms_admin, path: '/admin/cms' namespace :admin do resources :members end - get '/admin/orders' => 'admin/orders#index' - get '/admin/orders/:action' => 'admin/orders#:action' + get '/admin' => 'admin#index' get '/admin/newsletter' => 'admin#newsletter', as: :admin_newsletter get '/admin/:action' => 'admin#:action' diff --git a/db/migrate/20180118112809_add_datetaken_to_photos.rb b/db/migrate/20180118112809_add_datetaken_to_photos.rb new file mode 100644 index 000000000..42afaf23b --- /dev/null +++ b/db/migrate/20180118112809_add_datetaken_to_photos.rb @@ -0,0 +1,5 @@ +class AddDatetakenToPhotos < ActiveRecord::Migration + def change + add_column :photos, :date_taken, :datetime + end +end diff --git a/db/migrate/20180205000612_remove_shop.rb b/db/migrate/20180205000612_remove_shop.rb new file mode 100644 index 000000000..fa0fc8793 --- /dev/null +++ b/db/migrate/20180205000612_remove_shop.rb @@ -0,0 +1,9 @@ +class RemoveShop < ActiveRecord::Migration + def up + drop_table :order_items + drop_table :orders + drop_table :products + drop_table :account_types + drop_table :accounts + end +end diff --git a/db/migrate/20180213005731_seed_usage.rb b/db/migrate/20180213005731_seed_usage.rb new file mode 100644 index 000000000..f768451d2 --- /dev/null +++ b/db/migrate/20180213005731_seed_usage.rb @@ -0,0 +1,22 @@ +class SeedUsage < ActiveRecord::Migration + def change + # # seed can be all sown, meaning there is none left + add_column(:seeds, :finished, :boolean, default: false) + add_column(:seeds, :finished_at, :date, default: nil) + + # plantings can be grown from a seed + add_column(:plantings, :parent_seed_id, :integer) + add_foreign_key(:plantings, :seeds, + column: :parent_seed_id, + primary_key: :id, + name: :parent_seed, + on_delete: :nullify) + # seeds can be harvest from planting + add_column(:seeds, :parent_planting_id, :integer) + add_foreign_key(:seeds, :plantings, + column: :parent_planting_id, + primary_key: :id, + name: :parent_planting, + on_delete: :nullify) + end +end diff --git a/db/schema.rb b/db/schema.rb index dec08cc13..0651dbc56 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,27 +10,11 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171129041341) do +ActiveRecord::Schema.define(version: 20180213005731) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - create_table "account_types", id: :serial, force: :cascade do |t| - t.string "name", null: false - t.boolean "is_paid" - t.boolean "is_permanent_paid" - t.datetime "created_at" - t.datetime "updated_at" - end - - create_table "accounts", id: :serial, force: :cascade do |t| - t.integer "member_id", null: false - t.integer "account_type_id" - t.datetime "paid_until" - t.datetime "created_at" - t.datetime "updated_at" - end - create_table "alternate_names", id: :serial, force: :cascade do |t| t.string "name", null: false t.integer "crop_id", null: false @@ -339,25 +323,6 @@ ActiveRecord::Schema.define(version: 20171129041341) do t.datetime "updated_at" end - create_table "order_items", id: :serial, force: :cascade do |t| - t.integer "order_id" - t.integer "product_id" - t.integer "price" - t.integer "quantity" - t.datetime "created_at" - t.datetime "updated_at" - end - - create_table "orders", id: :serial, force: :cascade do |t| - t.datetime "created_at" - t.datetime "updated_at" - t.datetime "completed_at" - t.integer "member_id" - t.string "paypal_express_token" - t.string "paypal_express_payer_id" - t.string "referral_code" - end - create_table "orders_products", id: false, force: :cascade do |t| t.integer "order_id" t.integer "product_id" @@ -384,6 +349,7 @@ ActiveRecord::Schema.define(version: 20171129041341) do t.string "license_url" t.string "link_url", null: false t.string "flickr_photo_id" + t.datetime "date_taken" end create_table "photos_plantings", id: false, force: :cascade do |t| @@ -421,6 +387,7 @@ ActiveRecord::Schema.define(version: 20171129041341) do t.integer "lifespan" t.integer "days_to_first_harvest" t.integer "days_to_last_harvest" + t.integer "parent_seed_id" t.index ["slug"], name: "index_plantings_on_slug", unique: true end @@ -436,17 +403,6 @@ ActiveRecord::Schema.define(version: 20171129041341) do t.index ["slug"], name: "index_posts_on_slug", unique: true end - create_table "products", id: :serial, force: :cascade do |t| - t.string "name", null: false - t.text "description", null: false - t.integer "min_price", null: false - t.datetime "created_at" - t.datetime "updated_at" - t.integer "account_type_id" - t.integer "paid_months" - t.integer "recommended_price" - end - create_table "roles", id: :serial, force: :cascade do |t| t.string "name", null: false t.text "description" @@ -479,9 +435,14 @@ ActiveRecord::Schema.define(version: 20171129041341) do t.text "organic", default: "unknown" t.text "gmo", default: "unknown" t.text "heirloom", default: "unknown" + t.boolean "finished", default: false + t.date "finished_at" + t.integer "parent_planting_id" t.index ["slug"], name: "index_seeds_on_slug", unique: true end add_foreign_key "harvests", "plantings" add_foreign_key "photographings", "photos" + add_foreign_key "plantings", "seeds", column: "parent_seed_id", name: "parent_seed", on_delete: :nullify + add_foreign_key "seeds", "plantings", column: "parent_planting_id", name: "parent_planting", on_delete: :nullify end diff --git a/db/seeds.rb b/db/seeds.rb index a91c19048..4781bc80f 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -7,12 +7,9 @@ def load_data Crop.transaction do # for all Growstuff sites, including production ones load_roles - load_basic_account_types create_cropbot load_crops load_plant_parts - load_paid_account_types - load_products # We don't load these in an environment except development to # prevent creating users in the wild - especially admins - with @@ -44,20 +41,6 @@ def load_roles @wrangler = Role.create(name: 'Crop Wrangler') end -def load_basic_account_types - puts "Adding 'free' and 'staff' account types..." - AccountType.create!( - name: "Free", - is_paid: false, - is_permanent_paid: false - ) - AccountType.create!( - name: "Staff", - is_paid: true, - is_permanent_paid: true - ) -end - def load_test_users # rubocop:disable Metrics/AbcSize puts "Loading test users..." @@ -141,39 +124,6 @@ def create_cropbot @cropbot_user.skip_confirmation! @cropbot_user.roles << @wrangler @cropbot_user.save! - @cropbot_user.account.account_type = AccountType.find_by(name: "Staff") - @cropbot_user.account.save -end - -def load_paid_account_types - puts "Adding 'paid' and 'seed' account types..." - @paid_account = AccountType.create!( - name: "Paid", - is_paid: true, - is_permanent_paid: false - ) - @seed_account = AccountType.create!( - name: "Seed", - is_paid: true, - is_permanent_paid: true - ) -end - -def load_products - puts "Adding products..." - Product.create!( - name: "Annual subscription", - description: "Paid account, 1 year", - min_price: 3000, - account_type_id: @paid_account.id, - paid_months: 12 - ) - Product.create!( - name: "Seed account", - description: "Paid account, in perpetuity", - min_price: 15_000, - account_type_id: @seed_account.id - ) end def load_plant_parts diff --git a/lib/tasks/growstuff.rake b/lib/tasks/growstuff.rake index 03b2e3d0e..e6ec0650d 100644 --- a/lib/tasks/growstuff.rake +++ b/lib/tasks/growstuff.rake @@ -51,7 +51,7 @@ namespace :growstuff do send_on_day = 3 # wednesday every_n_weeks = 2 # send fortnightly - if Time.zone.today.cwday == send_on_day and Time.zone.today.cweek % every_n_weeks == 0 + if (Time.zone.today.cwday == send_on_day) && (Time.zone.today.cweek % every_n_weeks == 0) Member.confirmed.find_each do |m| Notifier.planting_reminder(m).deliver_now! end @@ -62,7 +62,7 @@ namespace :growstuff do # this fixes up anyone who has erroneously wound up with a 0,0 lat/long task depopulate_null_island: :environment do Member.find_each do |m| - if m.location and (m.latitude.nil? and m.longitude.nil?) + if m.location && (m.latitude.nil? && m.longitude.nil?) m.geocode m.save end @@ -88,84 +88,13 @@ namespace :growstuff do # site is small and there aren't many of them, so it shouldn't matter # for this one-off script. Garden.all.each do |g| - if g.name.nil? or g.name =~ /^\s*$/ + if g.name.nil? || g.name =~ /^\s*$/ g.name = "Garden" g.save end end end - desc "June 2013: create account types and products." - task setup_shop: :environment do - puts "Adding account types..." - AccountType.find_or_create_by( - name: "Free", - is_paid: false, - is_permanent_paid: false - ) - @paid_account = AccountType.find_or_create_by( - name: "Paid", - is_paid: true, - is_permanent_paid: false - ) - @seed_account = AccountType.find_or_create_by( - name: "Seed", - is_paid: true, - is_permanent_paid: true - ) - @staff_account = AccountType.find_or_create_by( - name: "Staff", - is_paid: true, - is_permanent_paid: true - ) - - puts "Adding products..." - Product.find_or_create_by( - name: "Annual subscription", - description: "An annual subscription gives you access "\ - "to paid account features for one year. Does not auto-renew.", - min_price: 3000, - account_type_id: @paid_account.id, - paid_months: 12 - ) - Product.find_or_create_by( - name: "Seed account", - description: "A seed account helps Growstuff grow in its "\ - "early days. It gives you all the features of "\ - "a paid account, in perpetuity. This account "\ - "type never expires.", - min_price: 15_000, - account_type_id: @seed_account.id - ) - - puts "Giving each member an account record..." - Member.all.each do |m| - Account.create(member_id: m.id) unless m.account - end - - puts "Making Skud a staff account..." - @skud = Member.find_by(login_name: 'Skud') - if @skud - @skud.account.account_type = @staff_account - @skud.account.save - end - - puts "Done setting up shop." - end - - desc "June 2013: replace nil account_types with free accounts" - task nil_account_type: :environment do - free = AccountType.find_by(name: "Free") - raise "Free account type not found: run rake growstuff:oneoff:setup_shop"\ - unless free - Account.all.each do |a| - unless a.account_type - a.account_type = free - a.save - end - end - end - desc "July 2013: replace nil seed.tradable_to with nowhere" task tradable_to_nowhere: :environment do Seed.all.each do |s| @@ -187,8 +116,6 @@ namespace :growstuff do task set_default_crop_creator: :environment do cropbot = Member.find_by(login_name: "cropbot") raise "cropbot not found: create cropbot member on site or run rake db:seed" unless cropbot - cropbot.account.account_type = AccountType.find_by(name: "Staff") # set this just because it's nice - cropbot.account.save Crop.find_each do |crop| unless crop.creator crop.creator = cropbot diff --git a/package-lock.json b/package-lock.json index e7029eb14..baf5adb61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -177,17 +177,6 @@ "dev": true, "requires": { "color-convert": "1.9.1" - }, - "dependencies": { - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - } } }, "supports-color": { @@ -270,6 +259,15 @@ "integrity": "sha1-dg8Cck9fCRG+fO+jSo4OEMXYUSo=", "dev": true }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha1-wSYRB66y8pTr/+ye2eytUppgl+0=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", @@ -1199,7 +1197,7 @@ "semver": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "integrity": "sha1-4FnAnYVx8FQII3M0M1BdOi8AsY4=", "dev": true }, "shebang-command": { diff --git a/spec/controllers/account_types_controller_spec.rb b/spec/controllers/account_types_controller_spec.rb deleted file mode 100644 index 010c8bf1f..000000000 --- a/spec/controllers/account_types_controller_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'rails_helper' - -describe AccountTypesController do - subject { response } - - context 'anon' do - describe '#index' do - before { get :index } - it { is_expected.not_to be_success } - end - end - context 'member' do - login_member(:member) - describe '#index' do - before { get :index } - it { is_expected.not_to be_success } - end - end - context 'admin' do - login_member(:admin_member) - describe '#index' do - let!(:aaa) { FactoryBot.create :account_type, name: 'aaa' } - let!(:zzz) { FactoryBot.create :account_type, name: 'zzz' } - - before { get :index } - it { is_expected.to be_success } - it { expect(assigns[:account_types].first).to eql(aaa) } - it { expect(assigns[:account_types].last).to eql(zzz) } - end - end -end diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb deleted file mode 100644 index 2b4222f1d..000000000 --- a/spec/controllers/accounts_controller_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'rails_helper' - -describe AccountsController do - login_member(:admin_member) - - def valid_attributes - { "paid_until" => Time.now } - end - - def create_account - # account details are automatically created when you create a new - # member; creating them manually will just cause errors as only one is - # allowed. This method has been left here in case it's useful in - # future. - member = FactoryBot.create(:member) - member.account - end -end diff --git a/spec/controllers/admin/orders_controller_spec.rb b/spec/controllers/admin/orders_controller_spec.rb deleted file mode 100644 index 207112f39..000000000 --- a/spec/controllers/admin/orders_controller_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'rails_helper' - -describe Admin::OrdersController do - login_member(:admin_member) - - describe "GET search" do - it "assigns @orders" do - order = FactoryBot.create(:order) - get :search, params: { search_by: 'order_id', search_text: order.id } - assigns(:orders).should eq([order]) - end - - it "sets an error message if nothing found" do - get :search, params: { search_by: 'order_id', search_text: 'foo' } - flash[:alert].should have_text "Couldn't find order with" - end - end -end diff --git a/spec/controllers/charts/crops_controller_spec b/spec/controllers/charts/crops_controller_spec new file mode 100644 index 000000000..5a4aa6548 --- /dev/null +++ b/spec/controllers/charts/crops_controller_spec @@ -0,0 +1,19 @@ +require 'rails_helper' + +describe Charts::CropsController do + describe 'GET charts' do + let(:crop) { FactoryBot.create :crop } + describe 'sunniness' do + before { get :sunniness, crop_id: crop.to_param } + it { expect(response).to be_success } + end + describe 'planted_from' do + before { get :planted_from, crop_id: crop.to_param } + it { expect(response).to be_success } + end + describe 'harvested_for' do + before { get :harvested_for, crop_id: crop.to_param } + it { expect(response).to be_success } + end + end +end diff --git a/spec/controllers/charts/gardens_controller_spec.rb b/spec/controllers/charts/gardens_controller_spec.rb new file mode 100644 index 000000000..daa50a811 --- /dev/null +++ b/spec/controllers/charts/gardens_controller_spec.rb @@ -0,0 +1,23 @@ +require 'rails_helper' + +describe Charts::GardensController do + include Devise::Test::ControllerHelpers + let(:valid_params) { { name: 'My second Garden' } } + + let(:garden) { FactoryBot.create :garden } + context "when not signed in" do + describe 'GET timeline' do + before { get :timeline, garden_id: garden.to_param } + it { expect(response).to be_success } + end + end + context "when signed in" do + before(:each) { sign_in member } + + let!(:member) { FactoryBot.create(:member) } + describe 'GET timeline' do + before { get :timeline, garden_id: garden.to_param } + it { expect(response).to be_success } + end + end +end diff --git a/spec/controllers/order_items_controller_spec.rb b/spec/controllers/order_items_controller_spec.rb deleted file mode 100644 index 9064a0f2a..000000000 --- a/spec/controllers/order_items_controller_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'rails_helper' - -describe OrderItemsController do - login_member(:admin_member) - - let(:member) { FactoryBot.create(:member) } - let(:product) { FactoryBot.create(:product) } - let(:order) { FactoryBot.create(:order, member: member) } - let(:order_item) do - FactoryBot.create(:order_item, - order: order, - product: product, - price: product.min_price) - end - - context 'signed in' do - before { sign_in member } - - describe "POST create" do - describe "redirects to order" do - before do - post :create, params: { - order_item: { order_id: order.id, product_id: product.id, price: product.min_price } - } - end - it { expect(response).to redirect_to(OrderItem.last.order) } - it { expect(OrderItem.last.order).to be_an_instance_of Order } - end - - describe 'creates an order for you' do - it do - expect do - post :create, params: { - order_item: { - product_id: product.id, - price: product.min_price - } - } - end.to change(Order, :count).by(1) - end - end - - describe "with non-int price" do - it "converts 3.33 to 333 cents" do - order = FactoryBot.create(:order, member: member) - product = FactoryBot.create(:product, min_price: 1) - expect do - post :create, params: { - order_item: { order_id: order.id, product_id: product.id, price: 3.33 } - } - end.to change(OrderItem, :count).by(1) - OrderItem.last.price.should eq 333 - end - end - end - end -end diff --git a/spec/controllers/orders_controller_spec.rb b/spec/controllers/orders_controller_spec.rb deleted file mode 100644 index a36365aad..000000000 --- a/spec/controllers/orders_controller_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -require 'rails_helper' - -describe OrdersController do - login_member(:admin_member) - - def valid_attributes - { "member_id" => 1 } - end - - def valid_session - {} - end - - describe "GET checkout" do - it 'sets the referral_code' do - member = FactoryBot.create(:member) - sign_in member - order = Order.create!(member_id: member.id) - get :checkout, params: { id: order.to_param, referral_code: 'FOOBAR' } - order.reload - order.referral_code.should eq 'FOOBAR' - end - - it "redirects to Paypal" do - member = FactoryBot.create(:member) - sign_in member - order = Order.create!(member_id: member.id) - get :checkout, params: { id: order.to_param } - response.status.should eq 302 - response.redirect_url.should match(/paypal\.com/) - end - end - - describe "GET complete" do - it "assigns the requested order as @order" do - member = FactoryBot.create(:member) - sign_in member - order = Order.create!(member_id: member.id) - get :complete, params: { id: order.to_param } - assigns(:order).should eq(order) - end - end - - describe "DELETE destroy" do - it "redirects to the shop" do - member = FactoryBot.create(:member) - sign_in member - order = Order.create!(member_id: member.id) - delete :destroy, params: { id: order.id } - response.should redirect_to(shop_url) - end - end -end diff --git a/spec/controllers/plantings_controller_spec.rb b/spec/controllers/plantings_controller_spec.rb index c1e4cd72b..8567a3386 100644 --- a/spec/controllers/plantings_controller_spec.rb +++ b/spec/controllers/plantings_controller_spec.rb @@ -85,6 +85,14 @@ describe PlantingsController do it { expect(assigns(:planting).planted_at).to eq Time.zone.today } end + context 'with parent seed' do + let(:seed) { FactoryBot.create :seed, owner: member } + before { get :new, seed_id: seed.to_param } + it { expect(assigns(:seed)).to eq(seed) } + end + end + + describe 'POST :create' do describe "sets the owner automatically" do before { post :create, params: { planting: valid_attributes } } it { expect(assigns(:planting).owner).to eq subject.current_member } diff --git a/spec/controllers/products_controller_spec.rb b/spec/controllers/products_controller_spec.rb deleted file mode 100644 index 173ef9ba4..000000000 --- a/spec/controllers/products_controller_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'rails_helper' - -describe ProductsController do - login_member(:admin_member) - - def valid_attributes - { - name: "product name", - description: 'some description', - min_price: 9.99 - } - end - - def valid_session - {} - end -end diff --git a/spec/controllers/seeds_controller_spec.rb b/spec/controllers/seeds_controller_spec.rb index 123516e43..3ff8051f1 100644 --- a/spec/controllers/seeds_controller_spec.rb +++ b/spec/controllers/seeds_controller_spec.rb @@ -1,6 +1,8 @@ require 'rails_helper' describe SeedsController do + let(:owner) { FactoryBot.create(:member) } + describe "GET index" do let(:owner) { FactoryBot.create(:member) } describe "picks up owner from params" do @@ -8,4 +10,20 @@ describe SeedsController do it { expect(assigns(:owner)).to eq(owner) } end end + + describe 'GET new' do + before { sign_in owner } + + it { expect(response).to be_success } + + context 'no parent planting' do + before { get :new } + end + + context 'with parent planting' do + let(:planting) { FactoryBot.create :planting, owner: owner } + before { get :new, planting_id: planting.to_param } + it { expect(assigns(:planting)).to eq(planting) } + end + end end diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb deleted file mode 100644 index f7e23b25f..000000000 --- a/spec/controllers/shop_controller_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -require 'rails_helper' - -describe ShopController do - let!(:product1) { FactoryBot.create(:product, name: 'aaa') } - let!(:product2) { FactoryBot.create(:product, name: 'zzz') } - - describe "GET index" do - describe 'not logged in' do - before { get :index, params: {} } - - describe "assigns all products as @products ordered by name" do - it { expect(assigns(:products)).to eq([product1, product2]) } - end - - describe "assigns a new @order_item to build forms" do - it { expect(assigns(:order_item)).to be_an_instance_of OrderItem } - end - - describe "assigns @order as nil if the user doesn't have one" do - it { expect(assigns(:order)).to be_nil } - end - end - describe 'logged in' do - describe "assigns @order as current_order if there is one" do - let(:member) { FactoryBot.create(:member) } - let!(:order) { FactoryBot.create(:order, member: member) } - - before do - sign_in member - get :index, params: {} - end - it { expect(assigns(:order)).to eq order } - end - end - end -end diff --git a/spec/factories/account_types.rb b/spec/factories/account_types.rb deleted file mode 100644 index 66717492f..000000000 --- a/spec/factories/account_types.rb +++ /dev/null @@ -1,33 +0,0 @@ -# Read about factories at https://github.com/thoughtbot/factory_bot - -FactoryBot.define do - factory :account_type do - name { Faker::Name.unique.name } - is_paid false - is_permanent_paid false - - factory :free_account_type do - name "Free" - is_paid false - is_permanent_paid false - end - - factory :paid_account_type do - name "Paid" - is_paid true - is_permanent_paid false - end - - factory :permanent_paid_account_type do - name "Permanent paid" - is_paid true - is_permanent_paid true - end - - factory :staff_account_type do - name "Staff" - is_paid true - is_permanent_paid true - end - end -end diff --git a/spec/factories/accounts.rb b/spec/factories/accounts.rb deleted file mode 100644 index fb4805964..000000000 --- a/spec/factories/accounts.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Read about factories at https://github.com/thoughtbot/factory_bot - -FactoryBot.define do - # never do this directly. - # create a member then look at member.account_detail instead. - # (because it's auto-created, and there can only be one.) -end diff --git a/spec/factories/order_items.rb b/spec/factories/order_items.rb deleted file mode 100644 index 5dfba00ab..000000000 --- a/spec/factories/order_items.rb +++ /dev/null @@ -1,10 +0,0 @@ -# Read about factories at https://github.com/thoughtbot/factory_bot - -FactoryBot.define do - factory :order_item do - order - product - price "999" - quantity 42 - end -end diff --git a/spec/factories/orders.rb b/spec/factories/orders.rb deleted file mode 100644 index 514eb4a22..000000000 --- a/spec/factories/orders.rb +++ /dev/null @@ -1,14 +0,0 @@ -# Read about factories at https://github.com/thoughtbot/factory_bot - -FactoryBot.define do - factory :order do - member - factory :completed_order do - completed_at '2013-05-08 01:01:01' - end - - factory :referred_order do - referral_code 'CAMPAIGN1' - end - end -end diff --git a/spec/factories/products.rb b/spec/factories/products.rb deleted file mode 100644 index 444c8597d..000000000 --- a/spec/factories/products.rb +++ /dev/null @@ -1,15 +0,0 @@ -# Read about factories at https://github.com/thoughtbot/factory_bot - -FactoryBot.define do - factory :product do - name "annual subscription" - description "paid membership, renewing yearly, *hurrah*" - min_price "999" - account_type - paid_months 12 - - factory :product_with_recommended_price do - recommended_price "1200" - end - end -end diff --git a/spec/factories/seeds.rb b/spec/factories/seeds.rb index 41b3d404f..b58193671 100644 --- a/spec/factories/seeds.rb +++ b/spec/factories/seeds.rb @@ -13,9 +13,17 @@ FactoryBot.define do heirloom 'unknown' days_until_maturity_min nil days_until_maturity_max nil + finished_at nil + + factory :finished_seed do + finished true + finished_at { Date.new } + end factory :tradable_seed do tradable_to "locally" + finished false + finished_at nil end factory :untradable_seed do diff --git a/spec/features/admin/account_types_spec.rb b/spec/features/admin/account_types_spec.rb deleted file mode 100644 index dac241586..000000000 --- a/spec/features/admin/account_types_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'rails_helper' - -feature "account types", js: true do - context "admin user" do - let(:member) { create :admin_member } - let(:account_type) { create :account_type } - - background do - login_as member - end - - scenario "navigating to account type admin with JavaScript on" do - visit root_path - # Extra click for the expandable login menu - click_link member.login_name - click_link "Admin" - expect(current_path).to eq admin_path - click_link "Account types" - expect(current_path).to eq account_types_path - end - - scenario "navigating to account type admin without JavaScript - Accessility version", js: false do - visit root_path - # Extra link not needed as menu is already expanded - click_link "Admin" - expect(current_path).to eq admin_path - click_link "Account types" - expect(current_path).to eq account_types_path - end - - scenario "adding an account type" do - visit account_types_path - click_link "New Account type" - expect(current_path).to eq new_account_type_path - fill_in 'Name', with: 'Guest' - click_button 'Save' - expect(current_path).to eq account_type_path(AccountType.last) - expect(page).to have_content 'Account type was successfully created' - end - - scenario 'editing account type' do - visit account_type_path account_type - click_link 'Edit' - fill_in 'Name', with: 'Something else' - click_button 'Save' - expect(current_path).to eq account_type_path(account_type) - expect(page).to have_content 'Account type was successfully updated' - expect(page).to have_content 'Something else' - end - - scenario 'deleting account type' do - visit account_type_path account_type - click_link 'Delete' - expect(current_path).to eq account_types_path - expect(page).to have_content 'Account type was successfully deleted' - end - end -end diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb deleted file mode 100644 index ca24daf83..000000000 --- a/spec/features/admin/products_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'rails_helper' - -feature "products" do - context "admin user" do - let(:member) { create :admin_member } - let(:product) { create :product } - - background do - login_as member - end - - scenario "navigating to product admin" do - visit admin_path - click_link "Products" - expect(current_path).to eq products_path - end - - scenario "adding a product" do - visit products_path - click_link "New Product" - expect(current_path).to eq new_product_path - fill_in 'Name', with: 'Special offer' - # note that failing to fill in a mandatory field has a messy error. - # This is not a priority defect but should be raised at some point. - fill_in 'Minimum price', with: '150' - click_button 'Save' - expect(current_path).to eq product_path(Product.last) - expect(page).to have_content 'Product was successfully created' - end - - scenario 'editing product' do - visit product_path product - click_link 'Edit' - fill_in 'Name', with: 'Something else' - click_button 'Save' - expect(current_path).to eq product_path(product) - expect(page).to have_content 'Product was successfully updated' - expect(page).to have_content 'Something else' - end - - scenario 'deleting product' - # this isn't possible. Should it be? - end -end diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index bbf37e996..b7dca2e8d 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -1,6 +1,15 @@ require 'rails_helper' feature "crop detail page", js: true do + subject do + # Update the medians after all the + # data has been loaded + crop.reload + crop.update_medians + + visit crop_path(crop) + page + end let(:crop) { create :crop } context "varieties" do @@ -187,16 +196,6 @@ feature "crop detail page", js: true do end end - subject do - # Update the medians after all the - # data has been loaded - crop.reload - crop.update_medians - - visit crop_path(crop) - page - end - context 'predictions' do let!(:planting) do FactoryBot.create(:planting, crop: crop, diff --git a/spec/features/crops/crop_photos_spec.rb b/spec/features/crops/crop_photos_spec.rb index 9d92449ea..81e506feb 100644 --- a/spec/features/crops/crop_photos_spec.rb +++ b/spec/features/crops/crop_photos_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' feature "crop detail page", js: true do + subject { page } let(:member) { create :member } let(:crop) { create :crop, plantings: [planting], harvests: [harvest] } @@ -39,7 +40,6 @@ feature "crop detail page", js: true do harvest.photos << photo4 visit crop_path(crop) end - subject { page } shared_examples "shows photos" do describe "show planting photos" do diff --git a/spec/features/gardens/actions_spec.rb b/spec/features/gardens/actions_spec.rb new file mode 100644 index 000000000..7342fe55e --- /dev/null +++ b/spec/features/gardens/actions_spec.rb @@ -0,0 +1,84 @@ +require 'rails_helper' +require 'custom_matchers' + +feature "Gardens" do + context 'logged in' do + let(:member) { FactoryBot.create :member } + background { login_as member } + subject { page } + let(:garden) { member.gardens.first } + + describe '#index' do + shared_examples "has buttons bar at top" do + it "has buttons bar at top" do + within '.layout-actions' do + is_expected.to have_link 'Add a garden' + is_expected.to have_link 'My Gardens' + is_expected.to have_link "Everyone's gardens" + end + end + end + + context 'my gardens' do + before { visit gardens_path(owner: member) } + include_examples "has buttons bar at top" + it "has actions on garden" do + within '.garden-actions' do + is_expected.to have_link 'Plant something' + is_expected.to have_link 'Mark as inactive' + is_expected.to have_link 'Edit' + is_expected.to have_link 'Add photo' + is_expected.to have_link 'Delete' + end + end + end + context 'all gardens' do + before { visit gardens_path } + include_examples "has buttons bar at top" + end + context "other member's garden" do + before { visit gardens_path(owner: FactoryBot.create(:member)) } + include_examples "has buttons bar at top" + it 'does not show actions on other member garden' do + is_expected.not_to have_link 'Plant something' + is_expected.not_to have_link 'Mark as inactive' + end + end + end + + describe '#show' do + end + end + + # background do + # login_as member + # visit new_garden_path + # end + + # it "has the required fields help text" do + # expect(page).to have_content "* denotes a required field" + # end + + # it "displays required and optional fields properly" do + # expect(page).to have_selector ".form-group.required", text: "Name" + # expect(page).to have_optional 'textarea#garden_description' + # expect(page).to have_optional 'input#garden_location' + # expect(page).to have_optional 'input#garden_area' + # end + + # scenario "Create new garden" do + # fill_in "Name", with: "New garden" + # click_button "Save" + # expect(page).to have_content "Garden was successfully created" + # expect(page).to have_content "New garden" + # end + + # scenario "Refuse to create new garden with negative area" do + # visit new_garden_path + # fill_in "Name", with: "Negative Garden" + # fill_in "Area", with: -5 + # click_button "Save" + # expect(page).not_to have_content "Garden was successfully created" + # expect(page).to have_content "Area must be greater than or equal to 0" + # end +end diff --git a/spec/features/gardens_spec.rb b/spec/features/gardens_spec.rb index 0954d35eb..368389719 100644 --- a/spec/features/gardens_spec.rb +++ b/spec/features/gardens_spec.rb @@ -59,7 +59,7 @@ feature "Planting a crop", js: true do end scenario "button on index to edit garden" do - first(".garden-info").click_link("edit_garden_link") + click_link href: edit_garden_path(garden) expect(page).to have_content 'Edit garden' end end @@ -68,7 +68,9 @@ feature "Planting a crop", js: true do visit new_garden_path fill_in "Name", with: "New garden" click_button "Save" - click_link 'edit_garden_link' + within '.garden-actions' do + click_link 'Edit' + end fill_in "Name", with: "Different name" click_button "Save" expect(page).to have_content "Garden was successfully updated" diff --git a/spec/features/harvests/browse_harvests_spec.rb b/spec/features/harvests/browse_harvests_spec.rb index 2a8400c2e..c51be9f41 100644 --- a/spec/features/harvests/browse_harvests_spec.rb +++ b/spec/features/harvests/browse_harvests_spec.rb @@ -4,19 +4,14 @@ feature "browse harvests" do let!(:member) { create :member } let!(:harvest) { create :harvest, owner: member } - background do - login_as member - end - + background { login_as member } + subject { page } feature 'blank optional fields' do let!(:harvest) { create :harvest, :no_description } - - before(:each) do - visit harvests_path - end + before { visit harvests_path } scenario 'read more' do - expect(page).not_to have_link "Read more" + is_expected.not_to have_link "Read more" end end @@ -28,11 +23,11 @@ feature "browse harvests" do end scenario 'read more' do - expect(page).to have_link "Read more" + is_expected.to have_link "Read more" end it 'links to #show' do - expect(page).to have_link harvest.crop.name, href: harvest_path(harvest) + is_expected.to have_link harvest.crop.name, href: harvest_path(harvest) end end end diff --git a/spec/features/harvests/harvesting_a_crop_spec.rb b/spec/features/harvests/harvesting_a_crop_spec.rb index 84c4b5a7f..e99c5e814 100644 --- a/spec/features/harvests/harvesting_a_crop_spec.rb +++ b/spec/features/harvests/harvesting_a_crop_spec.rb @@ -78,7 +78,9 @@ feature "Harvesting a crop", :js, :elasticsearch do scenario "Harvesting from planting page" do planting = create :planting, crop: maize, owner: member, garden: member.gardens.first visit planting_path(planting) - click_link "Harvest" + within ".planting-actions" do + click_link "Harvest" + end select plant_part.name, from: 'harvest[plant_part_id]' click_button "Save" diff --git a/spec/features/home/home_spec.rb b/spec/features/home/home_spec.rb new file mode 100644 index 000000000..4137b5e5a --- /dev/null +++ b/spec/features/home/home_spec.rb @@ -0,0 +1,84 @@ +require 'rails_helper' + +feature "home page" do + let(:member) { FactoryBot.create :member } + # let(:seed_photo) { FactoryBot.create :photo } + let(:photo) { FactoryBot.create :photo } + let(:crop) { FactoryBot.create :crop, created_at: 1.day.ago } + + let(:planting) { FactoryBot.create :planting, owner: member, crop: crop } + let(:seed) { FactoryBot.create :tradable_seed, owner: member, crop: crop } + let(:harvest) { FactoryBot.create :harvest, owner: member, crop: crop } + + let!(:tradable_seed) { FactoryBot.create :tradable_seed, finished: false } + let!(:finished_seed) { FactoryBot.create :tradable_seed, finished: true } + let!(:untradable_seed) { FactoryBot.create :untradable_seed } + background do + # Add photos, so they can appear on home page + planting.photos << photo + seed.photos << photo + harvest.photos << photo + end + + subject { page } + before { visit root_path } + + shared_examples 'shows seeds' do + it "show tradeable seed" do + is_expected.to have_link href: seed_path(tradable_seed) + end + it "does not show finished seeds" do + is_expected.not_to have_link href: seed_path(finished_seed) + end + it "does not show untradable seeds" do + is_expected.not_to have_link href: seed_path(untradable_seed) + end + + it { is_expected.to have_text 'View all seeds' } + end + + shared_examples 'show plantings' do + it 'shows plantings section' do + is_expected.to have_text 'Recently Planted' + is_expected.to have_link href: planting_path(planting) + end + end + shared_examples 'show harvests' do + it 'shows harvests section' do + is_expected.to have_text 'Recently Harvested' + is_expected.to have_link href: harvest_path(harvest) + end + end + + shared_examples "show crops" do + describe 'shows crops section' do + it { is_expected.to have_text 'Some of our crops' } + it { is_expected.to have_link href: crop_path(crop) } + end + describe 'shows recently added crops' do + it { is_expected.to have_text 'Recently Added' } + it 'link to newest crops' do + is_expected.to have_link crop.name, href: crop_path(crop) + end + end + it 'includes a link to all crops' do + is_expected.to have_link 'View all crops' + end + end + + context 'when anonymous' do + include_examples 'show crops' + include_examples 'show plantings' + include_examples 'show harvests' + include_examples 'shows seeds' + it { is_expected.to have_text 'community of food gardeners' } + end + + context "when signed in" do + background { login_as member } + include_examples 'show crops' + include_examples 'show plantings' + include_examples 'show harvests' + include_examples 'shows seeds' + end +end diff --git a/spec/features/member_profile_spec.rb b/spec/features/member_profile_spec.rb index 32b11bda1..9f1005894 100644 --- a/spec/features/member_profile_spec.rb +++ b/spec/features/member_profile_spec.rb @@ -9,7 +9,6 @@ feature "member profile", js: true do expect(page).to have_css("h1", text: member.login_name) expect(page).to have_content member.bio expect(page).to have_content "Member since: #{member.created_at.to_s(:date)}" - expect(page).to have_content "Account type: Free account" expect(page).to have_content "#{member.login_name}'s gardens" expect(page).to have_link "More about this garden...", href: garden_path(member.gardens.first) end @@ -141,10 +140,6 @@ feature "member profile", js: true do scenario "has a button to edit profile" do expect(page).to have_link "Edit profile", href: edit_member_registration_path end - - scenario "has a button to upgrade account" do - expect(page).to have_link "Upgrade account", href: shop_path - end end context "someone else's profile page" do diff --git a/spec/features/members/deletion_spec.rb b/spec/features/members/deletion_spec.rb index 793a6ada5..22b91a33b 100644 --- a/spec/features/members/deletion_spec.rb +++ b/spec/features/members/deletion_spec.rb @@ -10,7 +10,6 @@ feature "member deletion" do let!(:harvest) { FactoryBot.create(:harvest, owner: member) } let!(:seed) { FactoryBot.create(:seed, owner: member) } let!(:secondgarden) { FactoryBot.create(:garden, owner: member) } - let!(:order) { FactoryBot.create(:order, member: member, completed_at: Time.zone.now) } let(:admin) { FactoryBot.create(:admin_member) } background do @@ -115,16 +114,6 @@ feature "member deletion" do expect(page).to have_content "Member Deleted" end - scenario "leaves a record of orders and payments intact" do - login_as(admin) - visit admin_path - fill_in "search_text", with: member.login_name.to_s - find("#maincontainer").click_button("Search", exact: true) - expect(page).to have_content member.login_name.to_s - expect(page).to have_content "Found 1 result" - logout - end - scenario "can't be interesting" do expect(Member.interesting).not_to include(member) expect(Planting.interesting).not_to include(planting) diff --git a/spec/features/photos/new_photo_spec.rb b/spec/features/photos/new_photo_spec.rb index 1c13b2793..ee2a50691 100644 --- a/spec/features/photos/new_photo_spec.rb +++ b/spec/features/photos/new_photo_spec.rb @@ -33,7 +33,9 @@ feature "new photo page" do scenario "add photo" do visit garden_path(garden) - click_link "Add photo" + within '.garden-actions' do + click_link "Add photo" + end expect(page).to have_text garden.name end end diff --git a/spec/features/seeds/misc_seeds_spec.rb b/spec/features/seeds/misc_seeds_spec.rb index 1db053a80..ec1946d2c 100644 --- a/spec/features/seeds/misc_seeds_spec.rb +++ b/spec/features/seeds/misc_seeds_spec.rb @@ -5,9 +5,7 @@ feature "seeds", js: true do let(:member) { create :member } let(:crop) { create :crop } - background do - login_as member - end + background { login_as member } scenario "button on index to edit seed" do seed = create :seed, owner: member diff --git a/spec/features/seeds/seed_photos.rb b/spec/features/seeds/seed_photos.rb index 3d57f2cfe..7e26ece28 100644 --- a/spec/features/seeds/seed_photos.rb +++ b/spec/features/seeds/seed_photos.rb @@ -2,14 +2,13 @@ require 'rails_helper' require 'custom_matchers' feature "Seeds", :js do - let(:member) { FactoryBot.create :member } - let!(:seed) { FactoryBot.create :seed, owner: member } - subject do login_as member visit seed_path(seed) page end + let(:member) { FactoryBot.create :member } + let!(:seed) { FactoryBot.create :seed, owner: member } it { is_expected.to have_content 'Add photo' } diff --git a/spec/helpers/photos_helper_spec.rb b/spec/helpers/photos_helper_spec.rb new file mode 100644 index 000000000..589cf8104 --- /dev/null +++ b/spec/helpers/photos_helper_spec.rb @@ -0,0 +1,79 @@ +require 'rails_helper' + +describe PhotosHelper do + let(:crop) { FactoryBot.create :crop } + + let(:garden) { FactoryBot.create :garden } + let(:garden_photo) { FactoryBot.create(:photo, thumbnail_url: 'garden.jpg') } + let(:planting) { FactoryBot.create :planting, crop: crop } + let(:planting_photo) { FactoryBot.create(:photo, thumbnail_url: 'planting.jpg') } + let(:harvest) { FactoryBot.create :harvest, crop: crop } + let(:harvest_photo) { FactoryBot.create(:photo, thumbnail_url: 'harvest.jpg') } + let(:seed) { FactoryBot.create :seed, crop: crop } + let(:seed_photo) { FactoryBot.create(:photo, thumbnail_url: 'seed.jpg') } + + describe "crops" do + subject { crop_image_path(crop) } + + it { is_expected.to eq 'placeholder_150.png' } + + describe "with a planting" do + before { planting.photos << planting_photo } + it "uses planting photos" do + is_expected.to eq planting_photo.thumbnail_url + end + end + + describe "with a harvest photos" do + before { harvest.photos << harvest_photo } + it "uses harvest photos" do + is_expected.to eq harvest_photo.thumbnail_url + end + end + + describe "uses seed photo" do + before { seed.photos << seed_photo } + it "uses seed photos" do + is_expected.to eq seed_photo.thumbnail_url + end + end + end + + describe "gardens" do + subject { garden_image_path(garden) } + it { is_expected.to eq 'placeholder_150.png' } + + describe "uses garden's own photo" do + before { garden.photos << garden_photo } + it { is_expected.to eq garden_photo.thumbnail_url } + end + end + + describe 'plantings' do + subject { planting_image_path(planting) } + it { is_expected.to eq 'placeholder_150.png' } + describe "uses planting's own photo" do + before { planting.photos << planting_photo } + it { is_expected.to eq planting_photo.thumbnail_url } + end + end + + describe 'harvests' do + subject { harvest_image_path(harvest) } + it { is_expected.to eq 'placeholder_150.png' } + describe "uses harvest's own photo" do + before { harvest.photos << harvest_photo } + it { is_expected.to eq harvest_photo.thumbnail_url } + end + end + + describe 'seeds' do + subject { seed_image_path(seed) } + it { is_expected.to eq 'placeholder_150.png' } + + describe "uses seed's own photo" do + before { seed.photos << seed_photo } + it { is_expected.to eq seed_photo.thumbnail_url } + end + end +end diff --git a/spec/javascripts/support/jasmine.css b/spec/javascripts/support/jasmine.css deleted file mode 100644 index 4736015b6..000000000 --- a/spec/javascripts/support/jasmine.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - padding-top: 0; -} - -#jasmine_content { - width: 1000px; - height: 1000px; - position: absolute; - -webkit-transform: rotateY(-90deg); - -moz-transform: rotateY(-90deg); - -ms-transform: rotateY(-90deg); - transform: rotateY(-90deg); -} \ No newline at end of file diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml deleted file mode 100644 index 41a71db95..000000000 --- a/spec/javascripts/support/jasmine.yml +++ /dev/null @@ -1,123 +0,0 @@ -# src_files -# -# Return an array of filepaths relative to src_dir to include before jasmine specs. -# Default: [] -# -# EXAMPLE: -# -# src_files: -# - lib/source1.js -# - lib/source2.js -# - dist/**/*.js -# -src_files: - - assets/application.js - -# stylesheets -# -# Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs. -# Default: [] -# -# EXAMPLE: -# -# stylesheets: -# - css/style.css -# - stylesheets/*.css -# -stylesheets: - - assets/application.css - - spec/javascripts/support/jasmine.css -# helpers -# -# Return an array of filepaths relative to spec_dir to include before jasmine specs. -# Default: ["helpers/**/*.js"] -# -# EXAMPLE: -# -# helpers: -# - helpers/**/*.js -# -helpers: - - 'support/**/*.js' - -# spec_files -# -# Return an array of filepaths relative to spec_dir to include. -# Default: ["**/*[sS]pec.js"] -# -# EXAMPLE: -# -# spec_files: -# - **/*[sS]pec.js -# -spec_files: - - '**/*[sS]pec.js' - -# src_dir -# -# Source directory path. Your src_files must be returned relative to this path. Will use root if left blank. -# Default: project root -# -# EXAMPLE: -# -# src_dir: public -# -src_dir: - -# spec_dir -# -# Spec directory path. Your spec_files must be returned relative to this path. -# Default: spec/javascripts -# -# EXAMPLE: -# -# spec_dir: spec/javascripts -# -spec_dir: - -# spec_helper -# -# Ruby file that Jasmine server will require before starting. -# Returned relative to your root path -# Default spec/javascripts/support/jasmine_helper.rb -# -# EXAMPLE: -# -# spec_helper: spec/javascripts/support/jasmine_helper.rb -# -spec_helper: spec/javascripts/support/jasmine_helper.rb - -# boot_dir -# -# Boot directory path. Your boot_files must be returned relative to this path. -# Default: Built in boot file -# -# EXAMPLE: -# -# boot_dir: spec/javascripts/support/boot -# -boot_dir: - -# boot_files -# -# Return an array of filepaths relative to boot_dir to include in order to boot Jasmine -# Default: Built in boot file -# -# EXAMPLE -# -# boot_files: -# - '**/*.js' -# -boot_files: - -# rack_options -# -# Extra options to be passed to the rack server -# by default, Port and AccessLog are passed. -# -# This is an advanced options, and left empty by default -# -# EXAMPLE -# -# rack_options: -# server: 'thin' diff --git a/spec/javascripts/support/jasmine_helper.rb b/spec/javascripts/support/jasmine_helper.rb deleted file mode 100644 index 35ec28327..000000000 --- a/spec/javascripts/support/jasmine_helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -# Use this file to set/override Jasmine configuration options -# You can remove it if you don't need it. -# This file is loaded *after* jasmine.yml is interpreted. -# -# Example: using a different boot file. -# Jasmine.configure do |config| -# config.boot_dir = '/absolute/path/to/boot_dir' -# config.boot_files = lambda { ['/absolute/path/to/boot_dir/file.js'] } -# end -# -# Example: prevent PhantomJS auto install, uses PhantomJS already on your path. -# Jasmine.configure do |config| -# config.prevent_phantom_js_auto_install = true -# end - -Jasmine.configure do |config| - # Enable console.log for debugging - config.show_console_log = true -end diff --git a/spec/javascripts/support/vendor/jasmine-jquery.js b/spec/javascripts/support/vendor/jasmine-jquery.js deleted file mode 100644 index 89071b063..000000000 --- a/spec/javascripts/support/vendor/jasmine-jquery.js +++ /dev/null @@ -1,813 +0,0 @@ -/*! -Jasmine-jQuery: a set of jQuery helpers for Jasmine tests. - -Version 2.0.5 - -https://github.com/velesin/jasmine-jquery - -Copyright (c) 2010-2014 Wojciech Zawistowski, Travis Jeffery - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -+function (window, jasmine, $) { "use strict"; - - jasmine.spiedEventsKey = function (selector, eventName) { - return [$(selector).selector, eventName].toString() - } - - jasmine.getFixtures = function () { - return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures() - } - - jasmine.getStyleFixtures = function () { - return jasmine.currentStyleFixtures_ = jasmine.currentStyleFixtures_ || new jasmine.StyleFixtures() - } - - jasmine.Fixtures = function () { - this.containerId = 'jasmine-fixtures' - this.fixturesCache_ = {} - this.fixturesPath = 'spec/javascripts/fixtures' - } - - jasmine.Fixtures.prototype.set = function (html) { - this.cleanUp() - return this.createContainer_(html) - } - - jasmine.Fixtures.prototype.appendSet= function (html) { - this.addToContainer_(html) - } - - jasmine.Fixtures.prototype.preload = function () { - this.read.apply(this, arguments) - } - - jasmine.Fixtures.prototype.load = function () { - this.cleanUp() - this.createContainer_(this.read.apply(this, arguments)) - } - - jasmine.Fixtures.prototype.appendLoad = function () { - this.addToContainer_(this.read.apply(this, arguments)) - } - - jasmine.Fixtures.prototype.read = function () { - var htmlChunks = [] - , fixtureUrls = arguments - - for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { - htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex])) - } - - return htmlChunks.join('') - } - - jasmine.Fixtures.prototype.clearCache = function () { - this.fixturesCache_ = {} - } - - jasmine.Fixtures.prototype.cleanUp = function () { - $('#' + this.containerId).remove() - } - - jasmine.Fixtures.prototype.sandbox = function (attributes) { - var attributesToSet = attributes || {} - return $('
').attr(attributesToSet) - } - - jasmine.Fixtures.prototype.createContainer_ = function (html) { - var container = $('
') - .attr('id', this.containerId) - .html(html) - - $(document.body).append(container) - return container - } - - jasmine.Fixtures.prototype.addToContainer_ = function (html){ - var container = $(document.body).find('#'+this.containerId).append(html) - - if (!container.length) { - this.createContainer_(html) - } - } - - jasmine.Fixtures.prototype.getFixtureHtml_ = function (url) { - if (typeof this.fixturesCache_[url] === 'undefined') { - this.loadFixtureIntoCache_(url) - } - return this.fixturesCache_[url] - } - - jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) { - var self = this - , url = this.makeFixtureUrl_(relativeUrl) - , htmlText = '' - , request = $.ajax({ - async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded - cache: false, - url: url, - success: function (data, status, $xhr) { - htmlText = $xhr.responseText - } - }).fail(function ($xhr, status, err) { - throw new Error('Fixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')') - }) - - var scripts = $($.parseHTML(htmlText, true)).find('script[src]') || []; - - scripts.each(function(){ - $.ajax({ - async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded - cache: false, - dataType: 'script', - url: $(this).attr('src'), - success: function (data, status, $xhr) { - htmlText += '' - }, - error: function ($xhr, status, err) { - throw new Error('Script could not be loaded: ' + scriptSrc + ' (status: ' + status + ', message: ' + err.message + ')') - } - }); - }) - - self.fixturesCache_[relativeUrl] = htmlText; - } - - jasmine.Fixtures.prototype.makeFixtureUrl_ = function (relativeUrl){ - return this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl - } - - jasmine.Fixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) { - return this[methodName].apply(this, passedArguments) - } - - - jasmine.StyleFixtures = function () { - this.fixturesCache_ = {} - this.fixturesNodes_ = [] - this.fixturesPath = 'spec/javascripts/fixtures' - } - - jasmine.StyleFixtures.prototype.set = function (css) { - this.cleanUp() - this.createStyle_(css) - } - - jasmine.StyleFixtures.prototype.appendSet = function (css) { - this.createStyle_(css) - } - - jasmine.StyleFixtures.prototype.preload = function () { - this.read_.apply(this, arguments) - } - - jasmine.StyleFixtures.prototype.load = function () { - this.cleanUp() - this.createStyle_(this.read_.apply(this, arguments)) - } - - jasmine.StyleFixtures.prototype.appendLoad = function () { - this.createStyle_(this.read_.apply(this, arguments)) - } - - jasmine.StyleFixtures.prototype.cleanUp = function () { - while(this.fixturesNodes_.length) { - this.fixturesNodes_.pop().remove() - } - } - - jasmine.StyleFixtures.prototype.createStyle_ = function (html) { - var styleText = $('
').html(html).text() - , style = $('') - - this.fixturesNodes_.push(style) - $('head').append(style) - } - - jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache - jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read - jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_ - jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_ - jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_ - jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_ - - jasmine.getJSONFixtures = function () { - return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures() - } - - jasmine.JSONFixtures = function () { - this.fixturesCache_ = {} - this.fixturesPath = 'spec/javascripts/fixtures/json' - } - - jasmine.JSONFixtures.prototype.load = function () { - this.read.apply(this, arguments) - return this.fixturesCache_ - } - - jasmine.JSONFixtures.prototype.read = function () { - var fixtureUrls = arguments - - for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { - this.getFixtureData_(fixtureUrls[urlIndex]) - } - - return this.fixturesCache_ - } - - jasmine.JSONFixtures.prototype.clearCache = function () { - this.fixturesCache_ = {} - } - - jasmine.JSONFixtures.prototype.getFixtureData_ = function (url) { - if (!this.fixturesCache_[url]) this.loadFixtureIntoCache_(url) - return this.fixturesCache_[url] - } - - jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) { - var self = this - , url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl - - $.ajax({ - async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded - cache: false, - dataType: 'json', - url: url, - success: function (data) { - self.fixturesCache_[relativeUrl] = data - }, - error: function ($xhr, status, err) { - throw new Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')') - } - }) - } - - jasmine.JSONFixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) { - return this[methodName].apply(this, passedArguments) - } - - jasmine.jQuery = function () {} - - jasmine.jQuery.browserTagCaseIndependentHtml = function (html) { - return $('
').append(html).html() - } - - jasmine.jQuery.elementToString = function (element) { - return $(element).map(function () { return this.outerHTML; }).toArray().join(', ') - } - - var data = { - spiedEvents: {} - , handlers: [] - } - - jasmine.jQuery.events = { - spyOn: function (selector, eventName) { - var handler = function (e) { - data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = jasmine.util.argsToArray(arguments) - } - - $(selector).on(eventName, handler) - data.handlers.push(handler) - - return { - selector: selector, - eventName: eventName, - handler: handler, - reset: function (){ - delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] - } - } - }, - - args: function (selector, eventName) { - var actualArgs = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] - - if (!actualArgs) { - throw "There is no spy for " + eventName + " on " + selector.toString() + ". Make sure to create a spy using spyOnEvent." - } - - return actualArgs - }, - - wasTriggered: function (selector, eventName) { - return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]) - }, - - wasTriggeredWith: function (selector, eventName, expectedArgs, util, customEqualityTesters) { - var actualArgs = jasmine.jQuery.events.args(selector, eventName).slice(1) - - if (Object.prototype.toString.call(expectedArgs) !== '[object Array]') - actualArgs = actualArgs[0] - - return util.equals(expectedArgs, actualArgs, customEqualityTesters) - }, - - wasPrevented: function (selector, eventName) { - var args = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] - , e = args ? args[0] : undefined - - return e && e.isDefaultPrevented() - }, - - wasStopped: function (selector, eventName) { - var args = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] - , e = args ? args[0] : undefined - return e && e.isPropagationStopped() - }, - - cleanUp: function () { - data.spiedEvents = {} - data.handlers = [] - } - } - - var hasProperty = function (actualValue, expectedValue) { - if (expectedValue === undefined) - return actualValue !== undefined - - return actualValue === expectedValue - } - - beforeEach(function () { - jasmine.addMatchers({ - toHaveClass: function () { - return { - compare: function (actual, className) { - return { pass: $(actual).hasClass(className) } - } - } - }, - - toHaveCss: function () { - return { - compare: function (actual, css) { - for (var prop in css){ - var value = css[prop] - // see issue #147 on gh - ;if (value === 'auto' && $(actual).get(0).style[prop] === 'auto') continue - if ($(actual).css(prop) !== value) return { pass: false } - } - return { pass: true } - } - } - }, - - toBeVisible: function () { - return { - compare: function (actual) { - return { pass: $(actual).is(':visible') } - } - } - }, - - toBeHidden: function () { - return { - compare: function (actual) { - return { pass: $(actual).is(':hidden') } - } - } - }, - - toBeSelected: function () { - return { - compare: function (actual) { - return { pass: $(actual).is(':selected') } - } - } - }, - - toBeChecked: function () { - return { - compare: function (actual) { - return { pass: $(actual).is(':checked') } - } - } - }, - - toBeEmpty: function () { - return { - compare: function (actual) { - return { pass: $(actual).is(':empty') } - } - } - }, - - toBeInDOM: function () { - return { - compare: function (actual) { - return { pass: $.contains(document.documentElement, $(actual)[0]) } - } - } - }, - - toExist: function () { - return { - compare: function (actual) { - return { pass: $(actual).length } - } - } - }, - - toHaveLength: function () { - return { - compare: function (actual, length) { - return { pass: $(actual).length === length } - } - } - }, - - toHaveAttr: function () { - return { - compare: function (actual, attributeName, expectedAttributeValue) { - return { pass: hasProperty($(actual).attr(attributeName), expectedAttributeValue) } - } - } - }, - - toHaveProp: function () { - return { - compare: function (actual, propertyName, expectedPropertyValue) { - return { pass: hasProperty($(actual).prop(propertyName), expectedPropertyValue) } - } - } - }, - - toHaveId: function () { - return { - compare: function (actual, id) { - return { pass: $(actual).attr('id') == id } - } - } - }, - - toHaveHtml: function () { - return { - compare: function (actual, html) { - return { pass: $(actual).html() == jasmine.jQuery.browserTagCaseIndependentHtml(html) } - } - } - }, - - toContainHtml: function () { - return { - compare: function (actual, html) { - var actualHtml = $(actual).html() - , expectedHtml = jasmine.jQuery.browserTagCaseIndependentHtml(html) - - return { pass: (actualHtml.indexOf(expectedHtml) >= 0) } - } - } - }, - - toHaveText: function () { - return { - compare: function (actual, text) { - var actualText = $(actual).text() - var trimmedText = $.trim(actualText) - - if (text && $.isFunction(text.test)) { - return { pass: text.test(actualText) || text.test(trimmedText) } - } else { - return { pass: (actualText == text || trimmedText == text) } - } - } - } - }, - - toContainText: function () { - return { - compare: function (actual, text) { - var trimmedText = $.trim($(actual).text()) - - if (text && $.isFunction(text.test)) { - return { pass: text.test(trimmedText) } - } else { - return { pass: trimmedText.indexOf(text) != -1 } - } - } - } - }, - - toHaveValue: function () { - return { - compare: function (actual, value) { - return { pass: $(actual).val() === value } - } - } - }, - - toHaveData: function () { - return { - compare: function (actual, key, expectedValue) { - return { pass: hasProperty($(actual).data(key), expectedValue) } - } - } - }, - - toContainElement: function () { - return { - compare: function (actual, selector) { - if (window.debug) debugger - return { pass: $(actual).find(selector).length } - } - } - }, - - toBeMatchedBy: function () { - return { - compare: function (actual, selector) { - return { pass: $(actual).filter(selector).length } - } - } - }, - - toBeDisabled: function () { - return { - compare: function (actual, selector) { - return { pass: $(actual).is(':disabled') } - } - } - }, - - toBeFocused: function (selector) { - return { - compare: function (actual, selector) { - return { pass: $(actual)[0] === $(actual)[0].ownerDocument.activeElement } - } - } - }, - - toHandle: function () { - return { - compare: function (actual, event) { - var events = $._data($(actual).get(0), "events") - - if (!events || !event || typeof event !== "string") { - return { pass: false } - } - - var namespaces = event.split(".") - , eventType = namespaces.shift() - , sortedNamespaces = namespaces.slice(0).sort() - , namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") - - if (events[eventType] && namespaces.length) { - for (var i = 0; i < events[eventType].length; i++) { - var namespace = events[eventType][i].namespace - - if (namespaceRegExp.test(namespace)) - return { pass: true } - } - } else { - return { pass: (events[eventType] && events[eventType].length > 0) } - } - - return { pass: false } - } - } - }, - - toHandleWith: function () { - return { - compare: function (actual, eventName, eventHandler) { - var normalizedEventName = eventName.split('.')[0] - , stack = $._data($(actual).get(0), "events")[normalizedEventName] - - for (var i = 0; i < stack.length; i++) { - if (stack[i].handler == eventHandler) return { pass: true } - } - - return { pass: false } - } - } - }, - - toHaveBeenTriggeredOn: function () { - return { - compare: function (actual, selector) { - var result = { pass: jasmine.jQuery.events.wasTriggered(selector, actual) } - - result.message = result.pass ? - "Expected event " + $(actual) + " not to have been triggered on " + selector : - "Expected event " + $(actual) + " to have been triggered on " + selector - - return result; - } - } - }, - - toHaveBeenTriggered: function (){ - return { - compare: function (actual) { - var eventName = actual.eventName - , selector = actual.selector - , result = { pass: jasmine.jQuery.events.wasTriggered(selector, eventName) } - - result.message = result.pass ? - "Expected event " + eventName + " not to have been triggered on " + selector : - "Expected event " + eventName + " to have been triggered on " + selector - - return result - } - } - }, - - toHaveBeenTriggeredOnAndWith: function (j$, customEqualityTesters) { - return { - compare: function (actual, selector, expectedArgs) { - var wasTriggered = jasmine.jQuery.events.wasTriggered(selector, actual) - , result = { pass: wasTriggered && jasmine.jQuery.events.wasTriggeredWith(selector, actual, expectedArgs, j$, customEqualityTesters) } - - if (wasTriggered) { - var actualArgs = jasmine.jQuery.events.args(selector, actual, expectedArgs)[1] - result.message = result.pass ? - "Expected event " + actual + " not to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) : - "Expected event " + actual + " to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) - - } else { - // todo check on this - result.message = result.pass ? - "Expected event " + actual + " not to have been triggered on " + selector : - "Expected event " + actual + " to have been triggered on " + selector - } - - return result - } - } - }, - - toHaveBeenPreventedOn: function () { - return { - compare: function (actual, selector) { - var result = { pass: jasmine.jQuery.events.wasPrevented(selector, actual) } - - result.message = result.pass ? - "Expected event " + actual + " not to have been prevented on " + selector : - "Expected event " + actual + " to have been prevented on " + selector - - return result - } - } - }, - - toHaveBeenPrevented: function () { - return { - compare: function (actual) { - var eventName = actual.eventName - , selector = actual.selector - , result = { pass: jasmine.jQuery.events.wasPrevented(selector, eventName) } - - result.message = result.pass ? - "Expected event " + eventName + " not to have been prevented on " + selector : - "Expected event " + eventName + " to have been prevented on " + selector - - return result - } - } - }, - - toHaveBeenStoppedOn: function () { - return { - compare: function (actual, selector) { - var result = { pass: jasmine.jQuery.events.wasStopped(selector, actual) } - - result.message = result.pass ? - "Expected event " + actual + " not to have been stopped on " + selector : - "Expected event " + actual + " to have been stopped on " + selector - - return result; - } - } - }, - - toHaveBeenStopped: function () { - return { - compare: function (actual) { - var eventName = actual.eventName - , selector = actual.selector - , result = { pass: jasmine.jQuery.events.wasStopped(selector, eventName) } - - result.message = result.pass ? - "Expected event " + eventName + " not to have been stopped on " + selector : - "Expected event " + eventName + " to have been stopped on " + selector - - return result - } - } - } - }) - - jasmine.getEnv().addCustomEqualityTester(function(a, b) { - if (a && b) { - if (a instanceof $ || jasmine.isDomNode(a)) { - var $a = $(a) - - if (b instanceof $) - return $a.length == b.length && a.is(b) - - return $a.is(b); - } - - if (b instanceof $ || jasmine.isDomNode(b)) { - var $b = $(b) - - if (a instanceof $) - return a.length == $b.length && $b.is(a) - - return $(b).is(a); - } - } - }) - - jasmine.getEnv().addCustomEqualityTester(function (a, b) { - if (a instanceof $ && b instanceof $ && a.size() == b.size()) - return a.is(b) - }) - }) - - afterEach(function () { - jasmine.getFixtures().cleanUp() - jasmine.getStyleFixtures().cleanUp() - jasmine.jQuery.events.cleanUp() - }) - - window.readFixtures = function () { - return jasmine.getFixtures().proxyCallTo_('read', arguments) - } - - window.preloadFixtures = function () { - jasmine.getFixtures().proxyCallTo_('preload', arguments) - } - - window.loadFixtures = function () { - jasmine.getFixtures().proxyCallTo_('load', arguments) - } - - window.appendLoadFixtures = function () { - jasmine.getFixtures().proxyCallTo_('appendLoad', arguments) - } - - window.setFixtures = function (html) { - return jasmine.getFixtures().proxyCallTo_('set', arguments) - } - - window.appendSetFixtures = function () { - jasmine.getFixtures().proxyCallTo_('appendSet', arguments) - } - - window.sandbox = function (attributes) { - return jasmine.getFixtures().sandbox(attributes) - } - - window.spyOnEvent = function (selector, eventName) { - return jasmine.jQuery.events.spyOn(selector, eventName) - } - - window.preloadStyleFixtures = function () { - jasmine.getStyleFixtures().proxyCallTo_('preload', arguments) - } - - window.loadStyleFixtures = function () { - jasmine.getStyleFixtures().proxyCallTo_('load', arguments) - } - - window.appendLoadStyleFixtures = function () { - jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments) - } - - window.setStyleFixtures = function (html) { - jasmine.getStyleFixtures().proxyCallTo_('set', arguments) - } - - window.appendSetStyleFixtures = function (html) { - jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments) - } - - window.loadJSONFixtures = function () { - return jasmine.getJSONFixtures().proxyCallTo_('load', arguments) - } - - window.getJSONFixture = function (url) { - return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url] - } -}(window, window.jasmine, window.jQuery); diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index ef746c8bc..e95dfbdc6 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -71,201 +71,6 @@ describe Ability do end end - context "products" do - let(:product) { FactoryBot.create(:product) } - - context "standard member" do - it "can't read or manage products" do - ability.should_not be_able_to(:read, product) - ability.should_not be_able_to(:create, Product) - ability.should_not be_able_to(:update, product) - ability.should_not be_able_to(:destroy, product) - end - end - - context "admin" do - let(:role) { FactoryBot.create(:admin) } - - before do - member.roles << role - end - - it "has admin role" do - member.role?(:admin).should be true - end - - it "can read products" do - ability.should be_able_to(:read, product) - end - it "can create products" do - ability.should be_able_to(:create, Product) - end - it "can update products" do - ability.should be_able_to(:update, product) - end - it "can destroy products" do - ability.should be_able_to(:destroy, product) - end - end - end - - context "orders" do - let(:order) { FactoryBot.create(:order, member: member) } - let(:strangers_order) do - FactoryBot.create(:order, - member: FactoryBot.create(:member)) - end - let(:completed_order) do - FactoryBot.create(:completed_order, - member: member) - end - let(:order_item) { FactoryBot.create(:order_item, order: order) } - let(:strangers_order_item) do - FactoryBot.create(:order_item, - order: strangers_order) - end - let(:completed_order_item) do - FactoryBot.create(:order_item, - order: completed_order) - end - - context "standard member" do - it "can read their own orders" do - ability.should be_able_to(:read, order) - ability.should be_able_to(:read, completed_order) - end - - it "can't read other people's orders" do - ability.should_not be_able_to(:read, strangers_order) - end - - it "can create a new order" do - ability.should be_able_to(:create, Order) - end - - it "can complete their own current order" do - ability.should be_able_to(:complete, order) - end - - it "can't complete someone else's order" do - ability.should_not be_able_to(:complete, strangers_order) - end - - it "can't complete a completed order" do - ability.should_not be_able_to(:complete, completed_order) - end - - it "can delete a current order" do - ability.should be_able_to(:destroy, order) - end - - it "can't delete someone else's order" do - ability.should_not be_able_to(:destroy, strangers_order) - end - - it "can't delete a completed order" do - ability.should_not be_able_to(:destroy, completed_order) - end - - it "can't read their own order items" do - ability.should_not be_able_to(:read, order_item) - ability.should_not be_able_to(:read, completed_order_item) - end - - it "can't read other people's order items" do - ability.should_not be_able_to(:read, strangers_order_item) - end - - it "can create a new order item" do - ability.should be_able_to(:create, OrderItem) - end - - it "can't update their own order items" do - ability.should_not be_able_to(:update, order_item) - end - - it "can't update other people's order items" do - ability.should_not be_able_to(:update, strangers_order_item) - end - - it "can't updated items in completed orders" do - ability.should_not be_able_to(:update, completed_order_item) - end - - it "can't delete their own order item" do - ability.should_not be_able_to(:destroy, order_item) - end - - it "can't delete someone else's order item" do - ability.should_not be_able_to(:destroy, strangers_order_item) - end - - it "can't delete items from completed orders" do - ability.should_not be_able_to(:destroy, completed_order_item) - end - end - - context "admin" do - let(:role) { FactoryBot.create(:admin) } - - before do - member.roles << role - end - - it "has admin role" do - member.role?(:admin).should be true - end - - it "can read orders" do - ability.should be_able_to(:read, order) - end - - it "cannot create orders" do - ability.should_not be_able_to(:create, order) - end - - it "cannot complete orders" do - ability.should_not be_able_to(:complete, order) - end - - it "cannot delete orders" do - ability.should_not be_able_to(:destroy, order) - end - end - end - - context 'account details' do - let(:account) { member.account } - - context 'ordinary member' do - it "can't read account details" do - ability.should_not be_able_to(:read, account) - end - it "can't manage account details" do - ability.should_not be_able_to(:create, Account) - ability.should_not be_able_to(:update, account) - ability.should_not be_able_to(:destroy, account) - end - end - - context 'admin' do - let(:role) { FactoryBot.create(:admin) } - - before do - member.roles << role - end - - it "can read account details" do - ability.should be_able_to(:read, account) - end - it "can manage account details" do - ability.should be_able_to(:create, Account) - ability.should be_able_to(:update, account) - ability.should be_able_to(:destroy, account) - end - end - end - context 'plant parts' do let(:plant_part) { FactoryBot.create(:plant_part) } diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb deleted file mode 100644 index 15eb68084..000000000 --- a/spec/models/account_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'rails_helper' - -describe Account do - let(:member) { FactoryBot.create(:member) } - - it "auto-creates an account detail record when a member is created" do - member.account.should be_an_instance_of Account - end - - it "won't let you create two account details for the same member" do - @details = Account.new(member_id: member.id) - @details.should_not be_valid - end - - it "formats the 'paid until' date nicely" do - member.account.account_type = FactoryBot.create(:account_type) - member.account.paid_until_string.should eq nil - - member.account.account_type = FactoryBot.create(:permanent_paid_account_type) - member.account.paid_until_string.should eq "forever" - - member.account.account_type = FactoryBot.create(:paid_account_type) - @time = Time.zone.now - member.account.paid_until = @time - member.account.paid_until_string.should eq @time.to_s - end -end diff --git a/spec/models/account_type_spec.rb b/spec/models/account_type_spec.rb deleted file mode 100644 index c65c113ba..000000000 --- a/spec/models/account_type_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'rails_helper' - -describe AccountType do -end diff --git a/spec/models/crop_spec.rb b/spec/models/crop_spec.rb index 9e6173221..3f49a249d 100644 --- a/spec/models/crop_spec.rb +++ b/spec/models/crop_spec.rb @@ -276,6 +276,7 @@ describe Crop do end context 'interesting' do + subject { Crop.interesting } let(:photo) { FactoryBot.create :photo } # first, a couple of candidate crops let(:crop1) { FactoryBot.create(:crop) } @@ -284,8 +285,6 @@ describe Crop do let(:crop1_planting) { crop1.plantings.first } let(:crop2_planting) { crop2.plantings.first } - subject { Crop.interesting } - describe 'lists interesting crops' do before do # they need 3+ plantings each to be interesting diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index c0b082d13..e02935d8e 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -23,15 +23,6 @@ describe 'member' do expect(member.gardens.count).to eq 1 end - it 'should have a accounts entry' do - expect(member.account).to be_an_instance_of Account - end - - it "should have a default-type account by default" do - member.account.account_type.name.should eq Rails.application.config.default_account_type - expect(member.paid?).to eq false - end - it "doesn't show email by default" do expect(member.show_email).to eq false end @@ -292,89 +283,6 @@ describe 'member' do end end - context 'orders' do - it 'finds the current order' do - member = FactoryBot.create(:member) - FactoryBot.create(:completed_order, member: member) - order2 = FactoryBot.create(:order, member: member) - member.current_order.should eq order2 - end - - it "copes if there's no current order" do - member = FactoryBot.create(:member) - FactoryBot.create(:completed_order, member: member) - FactoryBot.create(:completed_order, member: member) - member.current_order.should be_nil - end - end - - context "paid accounts" do - let(:member) { FactoryBot.create(:member) } - - it "recognises a permanent paid account" do - account_type = FactoryBot.create(:account_type, - is_paid: true, is_permanent_paid: true) - member.account.account_type = account_type - member.paid?.should be(true) - end - - it "recognises a current paid account" do - account_type = FactoryBot.create(:account_type, - is_paid: true, is_permanent_paid: false) - member.account.account_type = account_type - member.account.paid_until = Time.zone.now + 1.month - member.paid?.should be(true) - end - - it "recognises an expired paid account" do - account_type = FactoryBot.create(:account_type, - is_paid: true, is_permanent_paid: false) - member.account.account_type = account_type - member.account.paid_until = Time.zone.now - 1.minute - member.paid?.should be(false) - end - - it "recognises a free account" do - account_type = FactoryBot.create(:account_type, - is_paid: false, is_permanent_paid: false) - member.account.account_type = account_type - member.paid?.should be(false) - end - - it "recognises a free account even with paid_until set" do - account_type = FactoryBot.create(:account_type, - is_paid: false, is_permanent_paid: false) - member.account.account_type = account_type - member.account.paid_until = Time.zone.now + 1.month - member.paid?.should be(false) - end - end - - context "update account" do - let(:product) do - FactoryBot.create(:product, - paid_months: 3) - end - let(:member) { FactoryBot.create(:member) } - - it "sets account_type" do - member.update_account_after_purchase(product) - member.account.account_type.should eq product.account_type - end - - it "sets paid_until" do - member.account.paid_until = nil # blank for now, as if never paid before - member.update_account_after_purchase(product) - - # stringify to avoid millisecond problems... - member.account.paid_until.to_s.should eq((Time.zone.now + 3.months).to_s) - - # and again to make sure it works for currently paid accounts - member.update_account_after_purchase(product) - member.account.paid_until.to_s.should eq((Time.zone.now + 3.months + 3.months).to_s) - end - end - context 'harvests' do it 'has harvests' do member = FactoryBot.create(:member) diff --git a/spec/models/order_item_spec.rb b/spec/models/order_item_spec.rb deleted file mode 100644 index 7c2de24c6..000000000 --- a/spec/models/order_item_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'rails_helper' - -describe OrderItem do - let(:order_item) { FactoryBot.create(:order_item) } - - it "has an order and a product" do - order_item.order.should be_an_instance_of Order - order_item.product.should be_an_instance_of Product - end - - it "validates price > product.min_price" do - @product = FactoryBot.create(:product) - order_item = FactoryBot.build(:order_item, price: @product.min_price - 1) - order_item.should_not be_valid - end - - it "doesn't let you add two items to an order" do - @product = FactoryBot.create(:product) - @order = FactoryBot.create(:order) - order_item = FactoryBot.build(:order_item, order: @order) - order_item.should be_valid - order_item.save - @order_item2 = FactoryBot.build(:order_item, order: @order) - @order_item2.should_not be_valid - end -end diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb deleted file mode 100644 index 9abb381ba..000000000 --- a/spec/models/order_spec.rb +++ /dev/null @@ -1,133 +0,0 @@ -require 'rails_helper' - -describe Order do - before(:each) do - @order = FactoryBot.create(:order) - @product = FactoryBot.create(:product) - @order_item = FactoryBot.create(:order_item, - order_id: @order.id, product_id: @product.id) - end - - describe '#by_member_id' do - before do - @member1 = FactoryBot.create(:member) - @member2 = FactoryBot.create(:member) - @order1 = Order.create!(member_id: @member1.id) - @order2 = Order.create!(member_id: @member2.id) - end - - it "only returns orders belonging to member" do - Order.by_member(@member1).should eq [@order1] - end - end - - it 'has order_items' do - @order.order_items.first.should eq @order_item - end - - it 'updates the account details' do - @member = FactoryBot.create(:member) - @order = FactoryBot.create(:order, member: @member) - @account_type = FactoryBot.create(:account_type, name: 'paid') - @product = FactoryBot.create(:product, - account_type: @account_type, - paid_months: 3) - @order_item = FactoryBot.create(:order_item, - order_id: @order.id, product_id: @product.id) - - @member.account.paid_until.should be_nil - - @order.update_account - - @member.account.account_type.should eq @account_type - @member.account.paid_until.should_not be_nil - end - - it "totals the amount due" do - @member = FactoryBot.create(:member) - @order = FactoryBot.create(:order, member: @member) - @product = FactoryBot.create(:product, - min_price: 1000) - # we force an order to only have one item at present. Add more if wanted - # later. - @order_item1 = FactoryBot.create(:order_item, - order_id: @order.id, product_id: @product.id, price: 1111, quantity: 1) - - @order.total.should eq 1111 - end - - it "gives the correct total for quantities more than 1" do - @member = FactoryBot.create(:member) - @order = FactoryBot.create(:order, member: @member) - @product = FactoryBot.create(:product, - min_price: 1000) - # we force an order to only have one item at present. Add more if wanted - # later. - @order_item1 = FactoryBot.create(:order_item, - order_id: @order.id, product_id: @product.id, price: 1111, quantity: 2) - - @order.total.should eq 2222 - end - - it "formats order items for activemerchant" do - @member = FactoryBot.create(:member) - @order = FactoryBot.create(:order, member: @member) - @product = FactoryBot.create(:product, - name: 'foo', - min_price: 1000) - # we force an order to only have one item at present. Add more if wanted - # later. - @order_item1 = FactoryBot.create(:order_item, - order_id: @order.id, product_id: @product.id, price: 1111, quantity: 1) - - @order.activemerchant_items.should eq [{ - name: 'foo', - quantity: 1, - amount: 1111 - }] - end - - context "referral codes" do - it "has a referral code" do - referred_order = FactoryBot.create(:referred_order) - referred_order.referral_code.should_not be nil - end - - it "validates referral codes" do - referred_order = FactoryBot.build(:order, referral_code: 'CAMP_AIGN1?') - referred_order.should_not be_valid - end - - it "cleans up messy referral codes" do - referred_order = FactoryBot.create(:order, referral_code: 'CaMpAiGn 1 ') - referred_order.referral_code.should eq 'CAMPAIGN1' - end - end - - context 'search' do - it 'finds orders by member' do - order = FactoryBot.create(:order) - Order.search(by: 'member', for: order.member.login_name).should eq [order] - end - - it 'finds orders by order_id' do - order = FactoryBot.create(:order) - Order.search(by: 'order_id', for: order.id).should eq [order] - end - - it 'finds orders by paypal_token' do - order = FactoryBot.create(:order, paypal_express_token: 'foo') - Order.search(by: 'paypal_token', for: 'foo').should eq [order] - end - - it 'finds orders by paypal_payer_id' do - order = FactoryBot.create(:order, paypal_express_payer_id: 'bar') - Order.search(by: 'paypal_payer_id', for: 'bar').should eq [order] - end - - it 'finds orders by referral_code' do - order = FactoryBot.create(:order, referral_code: 'baz') - Order.search(by: 'referral_code', for: 'baz').should eq [order] - end - end -end diff --git a/spec/models/photo_spec.rb b/spec/models/photo_spec.rb index c971ce2fd..5f07a3e29 100644 --- a/spec/models/photo_spec.rb +++ b/spec/models/photo_spec.rb @@ -50,7 +50,7 @@ describe Photo do it "automatically if unused" do photo.destroy_if_unused - expect(lambda { photo.reload }).to raise_error ActiveRecord::RecordNotFound + expect(-> { photo.reload }).to raise_error ActiveRecord::RecordNotFound end it 'they are used by plantings but not harvests' do @@ -58,7 +58,7 @@ describe Photo do planting.photos << photo harvest.destroy # photo is now used by harvest but not planting photo.destroy_if_unused - expect(lambda { photo.reload }).not_to raise_error + expect(-> { photo.reload }).not_to raise_error end it 'they are used by harvests but not plantings' do @@ -66,7 +66,7 @@ describe Photo do planting.photos << photo planting.destroy # photo is now used by harvest but not planting photo.destroy_if_unused - expect(lambda { photo.reload }).not_to raise_error + expect(-> { photo.reload }).not_to raise_error end it 'they are used by gardens but not plantings' do @@ -74,7 +74,7 @@ describe Photo do planting.photos << photo planting.destroy # photo is now used by garden but not planting photo.destroy_if_unused - expect(lambda { photo.reload }).not_to raise_error + expect(-> { photo.reload }).not_to raise_error end it 'they are no longer used by anything' do @@ -99,7 +99,7 @@ describe Photo do expect(photo.harvests.size).to eq 0 expect(photo.gardens.size).to eq 0 photo.destroy_if_unused - expect(lambda { photo.reload }).to raise_error ActiveRecord::RecordNotFound + expect(-> { photo.reload }).to raise_error ActiveRecord::RecordNotFound end it 'does not occur when a photo is still in use' do diff --git a/spec/models/planting_spec.rb b/spec/models/planting_spec.rb index 95e120c32..42bf04a4b 100644 --- a/spec/models/planting_spec.rb +++ b/spec/models/planting_spec.rb @@ -426,6 +426,22 @@ describe Planting do expect(Planting.joins(:owner).all).not_to include(planting) end + context 'ancestry' do + let(:parent_seed) { FactoryBot.create :seed } + let(:planting) { FactoryBot.create :planting, parent_seed: parent_seed } + it "planting has a parent seed" do + expect(planting.parent_seed).to eq(parent_seed) + end + it "seed has a child planting" do + expect(parent_seed.child_plantings).to eq [planting] + end + describe 'grandchildren' do + let(:grandchild_seed) { FactoryBot.create :seed, parent_planting: planting } + it { expect(grandchild_seed.parent_planting).to eq planting } + it { expect(grandchild_seed.parent_planting.parent_seed).to eq parent_seed } + end + end + # it 'predicts harvest times' do # crop = FactoryBot.create :crop # 10.times do diff --git a/spec/models/product_spec.rb b/spec/models/product_spec.rb deleted file mode 100644 index 2cc684667..000000000 --- a/spec/models/product_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'rails_helper' - -describe Product do - it "stringifies using the name" do - @product = FactoryBot.create(:product) - @product.to_s.should eq @product.name - end -end diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb index a7cfbecd3..76e309aa0 100644 --- a/spec/models/role_spec.rb +++ b/spec/models/role_spec.rb @@ -1,13 +1,12 @@ require 'rails_helper' describe Role do - let(:member) { FactoryBot.create(:member) } - subject do role = FactoryBot.create(:role, name: 'Crop Wrangler') role.members << member role end + let(:member) { FactoryBot.create(:member) } it 'has members' do subject.members.first.should eq member diff --git a/spec/models/seed_spec.rb b/spec/models/seed_spec.rb index d8cb1e35d..690e8c75f 100644 --- a/spec/models/seed_spec.rb +++ b/spec/models/seed_spec.rb @@ -158,4 +158,38 @@ describe Seed do Seed.has_photos.should include(seed) end end + + context 'ancestry' do + let(:parent_planting) { FactoryBot.create :planting } + let(:seed) { FactoryBot.create :seed, parent_planting: parent_planting } + it "seed has a parent planting" do + expect(seed.parent_planting).to eq(parent_planting) + end + it "planting has a child seed" do + expect(parent_planting.child_seeds).to eq [seed] + end + end + + context "finished" do + describe 'has finished fields' do + let(:seed) { FactoryBot.create(:finished_seed) } + it { expect(seed.finished).to eq true } + it { expect(seed.finished_at).to be_an_instance_of Date } + end + + describe 'scopes' do + let!(:seed) { FactoryBot.create(:seed) } + let!(:finished_seed) { FactoryBot.create(:finished_seed) } + + describe 'has finished scope' do + it { expect(Seed.finished).to include finished_seed } + it { expect(Seed.finished).not_to include seed } + end + + describe 'has current scope' do + it { expect(Seed.current).to include seed } + it { expect(Seed.current).not_to include finished_seed } + end + end + end end diff --git a/spec/requests/api/v1/crop_request_spec.rb b/spec/requests/api/v1/crop_request_spec.rb index 598a6993e..815ae8853 100644 --- a/spec/requests/api/v1/crop_request_spec.rb +++ b/spec/requests/api/v1/crop_request_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' RSpec.describe 'Plantings', type: :request do + subject { JSON.parse response.body } let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:crop) { FactoryBot.create :crop } let(:crop_encoded_as_json_api) do @@ -54,7 +55,6 @@ RSpec.describe 'Plantings', type: :request do } end - subject { JSON.parse response.body } describe '#index' do before { get '/api/v1/crops', params: {}, headers: headers } it { expect(subject['data']).to include(crop_encoded_as_json_api) } diff --git a/spec/requests/api/v1/gardens_request_spec.rb b/spec/requests/api/v1/gardens_request_spec.rb index ac1bb6786..e89deec3d 100644 --- a/spec/requests/api/v1/gardens_request_spec.rb +++ b/spec/requests/api/v1/gardens_request_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' RSpec.describe 'Gardens', type: :request do + subject { JSON.parse response.body } let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:garden) { FactoryBot.create :garden } let(:garden_encoded_as_json_api) do @@ -36,7 +37,6 @@ RSpec.describe 'Gardens', type: :request do "related" => "#{resource_url}/photos" } } end - subject { JSON.parse response.body } scenario '#index' do get '/api/v1/gardens', params: {}, headers: headers expect(subject['data']).to include(garden_encoded_as_json_api) diff --git a/spec/requests/api/v1/harvest_request_spec.rb b/spec/requests/api/v1/harvest_request_spec.rb index ebb0b59ba..833fe1928 100644 --- a/spec/requests/api/v1/harvest_request_spec.rb +++ b/spec/requests/api/v1/harvest_request_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' RSpec.describe 'Harvests', type: :request do + subject { JSON.parse response.body } let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:harvest) { FactoryBot.create :harvest } let(:harvest_encoded_as_json_api) do @@ -55,7 +56,6 @@ RSpec.describe 'Harvests', type: :request do } end - subject { JSON.parse response.body } describe '#index' do before { get '/api/v1/harvests', params: {}, headers: headers } it { expect(subject['data']).to include(harvest_encoded_as_json_api) } diff --git a/spec/requests/api/v1/member_request_spec.rb b/spec/requests/api/v1/member_request_spec.rb index 9536ec7c1..bbe01eeaf 100644 --- a/spec/requests/api/v1/member_request_spec.rb +++ b/spec/requests/api/v1/member_request_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' RSpec.describe 'Members', type: :request do + subject { JSON.parse response.body } let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:member) { FactoryBot.create :member } let(:member_encoded_as_json_api) do @@ -55,7 +56,6 @@ RSpec.describe 'Members', type: :request do } end - subject { JSON.parse response.body } describe '#index' do before { get '/api/v1/members', params: {}, headers: headers } it { expect(subject['data']).to include(member_encoded_as_json_api) } diff --git a/spec/requests/api/v1/photos_request_spec.rb b/spec/requests/api/v1/photos_request_spec.rb index 3af370a54..070b29be2 100644 --- a/spec/requests/api/v1/photos_request_spec.rb +++ b/spec/requests/api/v1/photos_request_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' RSpec.describe 'Photos', type: :request do + subject { JSON.parse response.body } let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:photo) { FactoryBot.create :photo } let(:photo_encoded_as_json_api) do @@ -53,7 +54,6 @@ RSpec.describe 'Photos', type: :request do } end - subject { JSON.parse response.body } describe '#index' do before { get '/api/v1/photos', params: {}, headers: headers } it { expect(subject['data']).to include(photo_encoded_as_json_api) } diff --git a/spec/requests/api/v1/plantings_request_spec.rb b/spec/requests/api/v1/plantings_request_spec.rb index ae561a66d..f84fa8092 100644 --- a/spec/requests/api/v1/plantings_request_spec.rb +++ b/spec/requests/api/v1/plantings_request_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' RSpec.describe 'Plantings', type: :request do + subject { JSON.parse response.body } let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:planting) { FactoryBot.create :planting } let(:planting_encoded_as_json_api) do @@ -66,7 +67,6 @@ RSpec.describe 'Plantings', type: :request do } end - subject { JSON.parse response.body } scenario '#index' do get '/api/v1/plantings', params: {}, headers: headers expect(subject['data']).to include(planting_encoded_as_json_api) diff --git a/spec/requests/api/v1/seeds_request_spec.rb b/spec/requests/api/v1/seeds_request_spec.rb index 243e645f5..f4ebcc2fd 100644 --- a/spec/requests/api/v1/seeds_request_spec.rb +++ b/spec/requests/api/v1/seeds_request_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' RSpec.describe 'Photos', type: :request do + subject { JSON.parse response.body } let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:seed) { FactoryBot.create :seed } let(:seed_encoded_as_json_api) do @@ -42,7 +43,6 @@ RSpec.describe 'Photos', type: :request do } end - subject { JSON.parse response.body } describe '#index' do before { get '/api/v1/seeds', params: {}, headers: headers } it { expect(subject['data']).to include(seed_encoded_as_json_api) } diff --git a/spec/routing/account_types_routing_spec.rb b/spec/routing/account_types_routing_spec.rb deleted file mode 100644 index 8402dd727..000000000 --- a/spec/routing/account_types_routing_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -require "rails_helper" - -describe AccountTypesController do - describe "routing" do - it "routes to #index" do - get("/account_types").should route_to("account_types#index") - end - - it "routes to #new" do - get("/account_types/new").should route_to("account_types#new") - end - - it "routes to #show" do - get("/account_types/1").should route_to("account_types#show", id: "1") - end - - it "routes to #edit" do - get("/account_types/1/edit").should route_to("account_types#edit", id: "1") - end - - it "routes to #create" do - post("/account_types").should route_to("account_types#create") - end - - it "routes to #update" do - put("/account_types/1").should route_to("account_types#update", id: "1") - end - - it "routes to #destroy" do - delete("/account_types/1").should route_to("account_types#destroy", id: "1") - end - end -end diff --git a/spec/routing/order_items_routing_spec.rb b/spec/routing/order_items_routing_spec.rb deleted file mode 100644 index 7487cb9f5..000000000 --- a/spec/routing/order_items_routing_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -require "rails_helper" - -describe OrderItemsController do - describe "routing" do - it "routes to #index" do - get("/order_items").should route_to("order_items#index") - end - - it "routes to #new" do - get("/order_items/new").should route_to("order_items#new") - end - - it "routes to #show" do - get("/order_items/1").should route_to("order_items#show", id: "1") - end - - it "routes to #edit" do - get("/order_items/1/edit").should route_to("order_items#edit", id: "1") - end - - it "routes to #create" do - post("/order_items").should route_to("order_items#create") - end - - it "routes to #update" do - put("/order_items/1").should route_to("order_items#update", id: "1") - end - - it "routes to #destroy" do - delete("/order_items/1").should route_to("order_items#destroy", id: "1") - end - end -end diff --git a/spec/routing/orders_routing_spec.rb b/spec/routing/orders_routing_spec.rb deleted file mode 100644 index 38184ef33..000000000 --- a/spec/routing/orders_routing_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -require "rails_helper" - -describe OrdersController do - describe "routing" do - it "routes to #index" do - get("/orders").should route_to("orders#index") - end - - it "routes to #new" do - get("/orders/new").should route_to("orders#new") - end - - it "routes to #show" do - get("/orders/1").should route_to("orders#show", id: "1") - end - - it "routes to #edit" do - get("/orders/1/edit").should route_to("orders#edit", id: "1") - end - - it "routes to #create" do - post("/orders").should route_to("orders#create") - end - - it "routes to #update" do - put("/orders/1").should route_to("orders#update", id: "1") - end - - it "routes to #destroy" do - delete("/orders/1").should route_to("orders#destroy", id: "1") - end - end -end diff --git a/spec/routing/products_routing_spec.rb b/spec/routing/products_routing_spec.rb deleted file mode 100644 index 15166a805..000000000 --- a/spec/routing/products_routing_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -require "rails_helper" - -describe ProductsController do - describe "routing" do - it "routes to #index" do - get("/products").should route_to("products#index") - end - - it "routes to #new" do - get("/products/new").should route_to("products#new") - end - - it "routes to #show" do - get("/products/1").should route_to("products#show", id: "1") - end - - it "routes to #edit" do - get("/products/1/edit").should route_to("products#edit", id: "1") - end - - it "routes to #create" do - post("/products").should route_to("products#create") - end - - it "routes to #update" do - put("/products/1").should route_to("products#update", id: "1") - end - - it "routes to #destroy" do - delete("/products/1").should route_to("products#destroy", id: "1") - end - end -end diff --git a/spec/views/account_types/edit.html.haml_spec.rb b/spec/views/account_types/edit.html.haml_spec.rb deleted file mode 100644 index 1bd22be03..000000000 --- a/spec/views/account_types/edit.html.haml_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'rails_helper' - -describe "account_types/edit" do - before(:each) do - @account_type = assign(:account_type, stub_model(AccountType, - name: "MyString", - is_paid: false, - is_permanent_paid: false)) - end - - it "renders the edit account_type form" do - render - - # Run the generator again with the --webrat flag if you want to use webrat matchers - assert_select "form", action: account_types_path(@account_type), method: "post" do - assert_select "input#account_type_name", name: "account_type[name]" - assert_select "input#account_type_is_paid", name: "account_type[is_paid]" - assert_select "input#account_type_is_permanent_paid", name: "account_type[is_permanent_paid]" - end - end -end diff --git a/spec/views/account_types/index.html.haml_spec.rb b/spec/views/account_types/index.html.haml_spec.rb deleted file mode 100644 index 56020fc35..000000000 --- a/spec/views/account_types/index.html.haml_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'rails_helper' - -describe "account_types/index" do - before(:each) do - @type = FactoryBot.create(:account_type) - assign(:account_types, [@type, @type]) - end - - it "renders a list of account_types" do - render - # Run the generator again with the --webrat flag if you want to use webrat matchers - assert_select "tr>td", text: @type.name.to_s, count: 2 - end -end diff --git a/spec/views/account_types/new.html.haml_spec.rb b/spec/views/account_types/new.html.haml_spec.rb deleted file mode 100644 index 027a1b7c1..000000000 --- a/spec/views/account_types/new.html.haml_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'rails_helper' - -describe "account_types/new" do - before(:each) do - assign(:account_type, stub_model(AccountType, - name: "MyString", - is_paid: false, - is_permanent_paid: false).as_new_record) - end - - it "renders new account_type form" do - render - - # Run the generator again with the --webrat flag if you want to use webrat matchers - assert_select "form", action: account_types_path, method: "post" do - assert_select "input#account_type_name", name: "account_type[name]" - assert_select "input#account_type_is_paid", name: "account_type[is_paid]" - assert_select "input#account_type_is_permanent_paid", name: "account_type[is_permanent_paid]" - end - end -end diff --git a/spec/views/account_types/show.html.haml_spec.rb b/spec/views/account_types/show.html.haml_spec.rb deleted file mode 100644 index a31d4bc33..000000000 --- a/spec/views/account_types/show.html.haml_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'rails_helper' - -describe "account_types/show" do - before(:each) do - @account_type = assign(:account_type, stub_model(AccountType, - name: "Name", - is_paid: false, - is_permanent_paid: false)) - end - - it "renders attributes in

" do - render - # Run the generator again with the --webrat flag if you want to use webrat matchers - rendered.should match(/Name/) - rendered.should match(/false/) - rendered.should match(/false/) - end -end diff --git a/spec/views/accounts/edit.html.haml_spec.rb b/spec/views/accounts/edit.html.haml_spec.rb deleted file mode 100644 index bc5c3b5a3..000000000 --- a/spec/views/accounts/edit.html.haml_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'rails_helper' - -describe "accounts/edit" do - before(:each) do - @member = FactoryBot.create(:member) - @account = assign(:account, @member.account) - end - - it "renders the edit account form" do - render - - # Run the generator again with the --webrat flag if you want to use webrat matchers - assert_select "form", action: accounts_path(@account), method: "post" do - assert_select "input#account_member_id", name: "account[member_id]" - assert_select "input#account_account_type", name: "account[account_type]" - end - end -end diff --git a/spec/views/accounts/index.html.haml_spec.rb b/spec/views/accounts/index.html.haml_spec.rb deleted file mode 100644 index 4ec76151e..000000000 --- a/spec/views/accounts/index.html.haml_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'rails_helper' - -describe "accounts/index" do - before(:each) do - @member = FactoryBot.create(:member) - @account = @member.account - assign(:accounts, [@account, @account]) - end - - it "renders a list of accounts" do - render - # Run the generator again with the --webrat flag if you want to use webrat matchers - assert_select "tr>td", text: @account.member_id.to_s, count: 2 - end -end diff --git a/spec/views/accounts/new.html.haml_spec.rb b/spec/views/accounts/new.html.haml_spec.rb deleted file mode 100644 index 77f6ff20f..000000000 --- a/spec/views/accounts/new.html.haml_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'rails_helper' - -describe "accounts/new" do - before(:each) do - @member = FactoryBot.create(:member) - assign(:account, @member.account) - end - - it "renders new account form" do - render - - # Run the generator again with the --webrat flag if you want to use webrat matchers - assert_select "form", action: accounts_path, method: "post" do - assert_select "input#account_member_id", name: "account[member_id]" - assert_select "input#account_account_type", name: "account[account_type]" - end - end -end diff --git a/spec/views/accounts/show.html.haml_spec.rb b/spec/views/accounts/show.html.haml_spec.rb deleted file mode 100644 index d96fbbd96..000000000 --- a/spec/views/accounts/show.html.haml_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'rails_helper' - -describe "accounts/show" do - before(:each) do - @member = FactoryBot.create(:member) - @account = assign(:account, @member.account) - end - - it "renders attributes in

" do - render - # Run the generator again with the --webrat flag if you want to use webrat matchers - rendered.should have_content @account.member_id.to_s - rendered.should have_content 'Free' - rendered.should have_content @account.paid_until.to_s - end -end diff --git a/spec/views/admin/index_spec.rb b/spec/views/admin/index_spec.rb index beb0eacbd..cc8b145d3 100644 --- a/spec/views/admin/index_spec.rb +++ b/spec/views/admin/index_spec.rb @@ -9,8 +9,6 @@ describe 'admin/index.html.haml', type: "view" do end it "includes links to manage various things" do - assert_select "a", href: account_types_path - assert_select "a", href: products_path assert_select "a", href: roles_path assert_select "a", href: forums_path end diff --git a/spec/views/admin/orders/index_spec.rb b/spec/views/admin/orders/index_spec.rb deleted file mode 100644 index 8d80dc3ef..000000000 --- a/spec/views/admin/orders/index_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'rails_helper' - -describe 'admin/orders/index.html.haml', type: "view" do - before(:each) do - @member = FactoryBot.create(:admin_member) - sign_in @member - controller.stub(:current_user) { @member } - render - end - - it "includes a search form for orders" do - assert_select "form" - assert_select "input#search_text" - assert_select "select#search_by" - end - - it "lets you search by referral code" do - assert_select "option[value=referral_code]", text: "Referral code" - end -end diff --git a/spec/views/crops/_planting_advice.html.haml_spec.rb b/spec/views/crops/_planting_advice.html.haml_spec.rb index 38499e2d9..12064a1b8 100644 --- a/spec/views/crops/_planting_advice.html.haml_spec.rb +++ b/spec/views/crops/_planting_advice.html.haml_spec.rb @@ -1,9 +1,8 @@ require 'rails_helper' describe "crops/_planting_advice" do - let(:planting) { FactoryBot.create(:planting) } - subject { rendered } + let(:planting) { FactoryBot.create(:planting) } shared_examples "render planting_advice" do before { render 'crops/planting_advice', crop: planting.crop } diff --git a/spec/views/gardens/show.html.haml_spec.rb b/spec/views/gardens/show.html.haml_spec.rb index 7130e2e4f..5594c6755 100644 --- a/spec/views/gardens/show.html.haml_spec.rb +++ b/spec/views/gardens/show.html.haml_spec.rb @@ -43,7 +43,7 @@ describe "gardens/show" do end it 'should have an edit button' do - rendered.should have_link 'edit_garden_link' + rendered.should have_link 'Edit' end it "shows a 'plant something' button" do diff --git a/spec/views/harvests/show.html.haml_spec.rb b/spec/views/harvests/show.html.haml_spec.rb index 624cf88c4..4aec453ff 100644 --- a/spec/views/harvests/show.html.haml_spec.rb +++ b/spec/views/harvests/show.html.haml_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' describe "harvests/show" do + subject { render } let!(:harvest) { FactoryBot.create(:harvest) } before do @@ -10,8 +11,6 @@ describe "harvests/show" do render end - subject { render } - describe "renders attributes" do it { is_expected.to have_content harvest.crop.name } it { is_expected.to have_content harvest.harvested_at.to_s } diff --git a/spec/views/home/_crops.html.haml_spec.rb b/spec/views/home/_crops.html.haml_spec.rb deleted file mode 100644 index bfc8dc856..000000000 --- a/spec/views/home/_crops.html.haml_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'rails_helper' - -describe 'home/_crops.html.haml', type: "view" do - let(:crop) { FactoryBot.create(:crop, name: 'pūhā') } - let(:photo) { FactoryBot.create(:photo) } - before(:each) do - plantings = FactoryBot.create_list(:planting, 3, crop: crop) - plantings.each do |p| - p.photos << photo - end - render - end - let(:planting) { crop.plantings.first } - it 'shows crops section' do - assert_select 'h2', text: 'Some of our crops' - assert_select "a[href='#{crop_path(crop)}']" - end - - it 'shows plantings section' do - assert_select 'h2', text: 'Recently planted' - rendered.should have_content planting.location - end - - it 'shows recently added crops' do - assert_select 'h2', text: 'Recently planted' - end - - it 'includes a link to all crops' do - assert_select "a[href='#{crops_path}']", text: /View all crops/ - end -end diff --git a/spec/views/orders/index.html.haml_spec.rb b/spec/views/orders/index.html.haml_spec.rb deleted file mode 100644 index c95f204e2..000000000 --- a/spec/views/orders/index.html.haml_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'rails_helper' - -describe "orders/index" do - before(:each) do - @member = FactoryBot.create(:member) - sign_in @member - @order1 = FactoryBot.create(:order, member: @member) - @order2 = FactoryBot.create(:completed_order, member: @member) - assign(:orders, [@order1, @order2]) - end - - it "shows your current account status" do - render - rendered.should have_content "Your current account status" - end - - it "renders a list of orders" do - render - # Run the generator again with the --webrat flag if you want to use webrat matchers - assert_select "tr>td a/@href", text: "/orders/#{@order1.id}" - assert_select "tr>td a/@href", text: "/orders/#{@order2.id}" - end -end diff --git a/spec/views/orders/show.html.haml_spec.rb b/spec/views/orders/show.html.haml_spec.rb deleted file mode 100644 index 4f9c7d7f6..000000000 --- a/spec/views/orders/show.html.haml_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'rails_helper' - -describe "orders/show" do - before(:each) do - @member = FactoryBot.create(:member) - sign_in @member - controller.stub(:current_user) { @member } - end - - context "current order" do - before(:each) do - @order = assign(:order, FactoryBot.create(:order, member: @member)) - @order_item = FactoryBot.create(:order_item, - order: @order, - quantity: 2, - price: 9900) - render - end - - it "displays order number" do - rendered.should have_content "Order number" - end - - it "shows order items in a table" do - assert_select "table>tr>th", text: "Product" - end - - it "shows the total" do - rendered.should have_content "Total:" - assert_select "strong", /198.00/ - end - - it "shows a foreign exchange link for the total" do - currency = Rails.application.config.currency - assert_select("a[href='http://www.wolframalpha.com/input/?i=198.00+#{currency}']") - end - - it "asks for a referral code" do - assert_select "input[id='referral_code']" - end - - it "shows a checkout button" do - assert_select "input[value='Checkout with PayPal']" - end - - it "shows a delete order button" do - assert_select "a", text: "Delete this order" - end - end - - context "completed order" do - before(:each) do - @order = assign(:order, FactoryBot.create(:completed_order, member: @member)) - @order_item = FactoryBot.create(:order_item, - order: @order, - quantity: 2, - price: 9900) - render - end - - it "doesn't show a checkout button" do - assert_select "a", text: "Checkout", count: 0 - end - - it "doesn't show delete order button" do - assert_select "a", text: "Delete this order", count: 0 - end - end -end diff --git a/spec/views/plantings/_form.html.haml_spec.rb b/spec/views/plantings/_form.html.haml_spec.rb index 692b53173..6371311ae 100644 --- a/spec/views/plantings/_form.html.haml_spec.rb +++ b/spec/views/plantings/_form.html.haml_spec.rb @@ -2,7 +2,6 @@ require 'rails_helper' describe "plantings/_form" do before(:each) do - controller.stub(:current_user) { nil } @member = FactoryBot.create(:member) @garden = FactoryBot.create(:garden, owner: @member) @uppercase = FactoryBot.create(:uppercasecrop) @@ -14,6 +13,8 @@ describe "plantings/_form" do crop: @crop, owner: @member, planted_at: Date.new(2013, 3, 1)) + + sign_in @member render end diff --git a/spec/views/plantings/show.html.haml_spec.rb b/spec/views/plantings/show.html.haml_spec.rb index 899724ac7..47a742c58 100644 --- a/spec/views/plantings/show.html.haml_spec.rb +++ b/spec/views/plantings/show.html.haml_spec.rb @@ -35,11 +35,10 @@ describe "plantings/show" do rendered.should have_content 'cutting' end - it "doesn't show planted_from if blank" do + it "shows planted_from if blank" do planting.update(planted_from: '') render - rendered.should_not have_content 'Planted from:' - rendered.should_not have_content 'cutting' + rendered.should have_content 'Planted from: not specified' end end diff --git a/spec/views/posts/show.html.haml_spec.rb b/spec/views/posts/show.html.haml_spec.rb index 79f352486..aaad30fab 100644 --- a/spec/views/posts/show.html.haml_spec.rb +++ b/spec/views/posts/show.html.haml_spec.rb @@ -1,13 +1,13 @@ require 'rails_helper' describe "posts/show" do + subject { rendered } let(:author) { FactoryBot.create(:member, login_name: 'mary') } before(:each) do controller.stub(:current_user) { nil } assign(:post, post) end - subject { rendered } describe 'render post' do before { render } diff --git a/spec/views/products/edit.html.haml_spec.rb b/spec/views/products/edit.html.haml_spec.rb deleted file mode 100644 index 3d60c9e95..000000000 --- a/spec/views/products/edit.html.haml_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'rails_helper' - -describe "products/edit" do - before(:each) do - @product = assign(:product, stub_model(Product, - name: "MyString", - description: "MyString", - min_price: "9.99")) - end - - it "renders the edit product form" do - render - - # Run the generator again with the --webrat flag if you want to use webrat matchers - assert_select "form", action: products_path(@product), method: "post" do - assert_select "input#product_name", name: "product[name]" - assert_select "textarea#product_description", name: "product[description]" - assert_select "input#product_min_price", name: "product[min_price]" - assert_select "input#product_recommended_price", name: "product[recommended_price]" - end - end -end diff --git a/spec/views/products/index.html.haml_spec.rb b/spec/views/products/index.html.haml_spec.rb deleted file mode 100644 index 90e08e2a3..000000000 --- a/spec/views/products/index.html.haml_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'rails_helper' - -describe "products/index" do - before(:each) do - @product = FactoryBot.create(:product) - assign(:products, [@product, @product]) - end - - it "renders a list of products" do - render - # Run the generator again with the --webrat flag if you want to use webrat matchers - assert_select "tr>td", text: @product.name, count: 2 - assert_select "tr>td", text: @product.description, count: 2 - assert_select "tr>td", text: @product.min_price.to_s, count: 2 - end -end diff --git a/spec/views/products/new.html.haml_spec.rb b/spec/views/products/new.html.haml_spec.rb deleted file mode 100644 index 336c6bac1..000000000 --- a/spec/views/products/new.html.haml_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'rails_helper' - -describe "products/new" do - before(:each) do - assign(:product, stub_model(Product, - name: "MyString", - description: "MyString", - min_price: "9.99").as_new_record) - end - - it "renders new product form" do - render - - # Run the generator again with the --webrat flag if you want to use webrat matchers - assert_select "form", action: products_path, method: "post" do - assert_select "input#product_name", name: "product[name]" - assert_select "textarea#product_description", name: "product[description]" - assert_select "input#product_min_price", name: "product[min_price]" - assert_select "input#product_recommended_price", name: "product[recommended_price]" - assert_select "select#product_account_type_id", name: "product[account_type_id]" - assert_select "input#product_paid_months", name: "product[paid_months]" - end - end -end diff --git a/spec/views/products/show.html.haml_spec.rb b/spec/views/products/show.html.haml_spec.rb deleted file mode 100644 index 315d9caae..000000000 --- a/spec/views/products/show.html.haml_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'rails_helper' - -describe "products/show" do - before(:each) do - @product = assign(:product, FactoryBot.create(:product)) - end - - it "renders attributes in

" do - render - # Run the generator again with the --webrat flag if you want to use webrat matchers - rendered.should have_content @product.name - rendered.should have_content @product.min_price.to_s - rendered.should have_content @product.recommended_price.to_s - end -end diff --git a/spec/views/shop/index_spec.rb b/spec/views/shop/index_spec.rb deleted file mode 100644 index 29b740c92..000000000 --- a/spec/views/shop/index_spec.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'rails_helper' - -describe 'shop/index.html.haml', type: "view" do - before(:each) do - @product1 = FactoryBot.create(:product) - @product2 = FactoryBot.create(:product_with_recommended_price) - assign(:products, [@product1, @product2]) - assign(:order_item, OrderItem.new) - end - - context "signed in" do - before(:each) do - @member = FactoryBot.create(:member) - controller.stub(:current_user) { @member } - render - end - - it 'shows products' do - assert_select("h2", text: @product1.name) - end - - it 'shows prices in configured currency' do - rendered.should have_content format('9.99 %s', Rails.application.config.currency) - end - - it 'should contain an exchange rate link' do - currency = Rails.application.config.currency - assert_select("a[href='http://www.wolframalpha.com/input/?i=9.99+#{currency}']") - end - - it 'shows recommended price for products that have it' do - rendered.should have_content format('12.00 %s', Rails.application.config.currency) - end - - it 'should contain an exchange rate link for recommended price' do - currency = Rails.application.config.currency - assert_select("a[href='http://www.wolframalpha.com/input/?i=12.00+#{currency}']") - end - - it 'displays the order form' do - assert_select "form", count: 2 - end - - it 'renders markdown in product descriptions' do - assert_select "em", text: 'hurrah', count: 2 - end - end - - context "is paid" do - before(:each) do - @member = FactoryBot.create(:member) - @member.account.account_type = FactoryBot.create(:paid_account_type) - @member.account.paid_until = Time.zone.now + 1.year - controller.stub(:current_member) { @member } - end - - it "recognises the paid member" do - @member.paid?.should be(true) - end - - it "tells you you have a paid membership" do - render - rendered.should have_content "You currently have a paid" - end - - it "doesn't show shop" do - render - assert_select "form", false - end - end - - context "signed out" do - before(:each) do - controller.stub(:current_user) { nil } - render - end - - it "tells you to sign up/sign in" do - rendered.should have_content "sign in or sign up" - end - end -end