diff --git a/app/controllers/closet_hangers_controller.rb b/app/controllers/closet_hangers_controller.rb index 98f606b9..6b60f4ac 100644 --- a/app/controllers/closet_hangers_controller.rb +++ b/app/controllers/closet_hangers_controller.rb @@ -22,16 +22,35 @@ class ClosetHangersController < ApplicationController unless @closet_hanger.quantity == 0 # save the hanger, new record or not if @closet_hanger.save - flash[:success] = "Success! You own #{@closet_hanger.quantity} #{@item.name.pluralize}." + respond_to do |format| + format.html { + flash[:success] = "Success! You own #{@closet_hanger.quantity} #{@item.name.pluralize}." + redirect_back! + } + + format.json { render :json => true } + end else - flash[:alert] = "We couldn't save how many of this item you own: #{@closet_hanger.errors.full_messages.to_sentence}" + respond_to do |format| + format.html { + flash[:alert] = "We couldn't save how many of this item you own: #{@closet_hanger.errors.full_messages.to_sentence}" + redirect_back! + } + + format.json { render :json => {:errors => @closet_hanger.errors.full_messages}, :status => :unprocessable_entity } + end end else # delete the hanger since the user doesn't want it @closet_hanger.destroy - flash[:success] = "Success! You do not own #{@item.name}." - end + respond_to do |format| + format.html { + flash[:success] = "Success! You do not own #{@item.name}." + redirect_back! + } - redirect_to params[:return_to] || @item + format.json { render :json => true } + end + end end alias_method :create, :update @@ -41,5 +60,9 @@ class ClosetHangersController < ApplicationController def authorize_user! raise AccessDenied unless user_signed_in? && current_user.id == params[:user_id].to_i end + + def redirect_back! + redirect_to params[:return_to] || @item + end end diff --git a/app/stylesheets/_layout.sass b/app/stylesheets/_layout.sass index 62e30d25..9a3eaaf7 100644 --- a/app/stylesheets/_layout.sass +++ b/app/stylesheets/_layout.sass @@ -157,8 +157,8 @@ ul.buttons .object +inline-block - margin: .5em 0 - padding: 0 .5em + margin: $object-padding 0 + padding: 0 $object-padding position: relative text-align: center vertical-align: top diff --git a/app/stylesheets/closet_hangers/_index.sass b/app/stylesheets/closet_hangers/_index.sass index 8f489f88..6470149a 100644 --- a/app/stylesheets/closet_hangers/_index.sass +++ b/app/stylesheets/closet_hangers/_index.sass @@ -24,8 +24,9 @@ body.closet_hangers-index padding: 2px 4px position: absolute left: ($object-width - $object-img-size) / 2 + $object-padding + line-height: 1 text-align: left - top: $object-img-size - 24px + top: $object-img-size - 20px form display: none @@ -35,23 +36,37 @@ body.closet_hangers-index font-weight: bold &.current-user - .object:hover - .quantity - +opacity(1) - background: transparent - top: $object-img-size - 25px - padding: 0 + .object:hover .quantity + +opacity(1) + background: transparent + top: $object-img-size - 24px + padding: 0 - span - display: none + span + display: none - form - display: inline + form + display: inline - input[type=number] - padding: 2px - width: 2em + input[type=number] + padding: 2px + width: 2em + + input[type=submit] + font-size: 85% + + &.js + .object:hover .quantity + input[type=number] + width: 2.5em input[type=submit] - font-size: 85% + display: none + + .object.loading + background: $module-bg-color + outline: 1px solid $module-border-color + + .quantity span:after + content: "…" diff --git a/app/stylesheets/partials/clean/_constants.sass b/app/stylesheets/partials/clean/_constants.sass index 877b5b2f..f0595da9 100644 --- a/app/stylesheets/partials/clean/_constants.sass +++ b/app/stylesheets/partials/clean/_constants.sass @@ -24,7 +24,8 @@ $text-font: "Droid Serif", Georgia, "Times New Roman", Times, serif $object-img-size: 80px $object-width: 100px -$object-padding: 6px +$object-padding: 8px $nc-icon-size: 16px $container-top-padding: 3em + diff --git a/app/views/closet_hangers/index.html.haml b/app/views/closet_hangers/index.html.haml index df705da8..c5d600d9 100644 --- a/app/views/closet_hangers/index.html.haml +++ b/app/views/closet_hangers/index.html.haml @@ -4,8 +4,13 @@ = link_to "Import closet from Neopets", new_closet_page_path, :id => 'import-link' - else - title "#{@user.name}'s Items" + #closet-hangers{:class => user_is?(@user) ? 'current-user' : nil} - if !@closet_hangers.empty? + - if user_is?(@user) + %p + These are the items you own. Hover over an item to remove it from the + list or to change the quantity. = render @closet_hangers - else - if user_is?(@user) @@ -22,3 +27,7 @@ - else %p #{@user.name} hasn't tracked any items on Dress to Impress. +- content_for :javascripts do + = include_javascript_libraries :jquery + = javascript_include_tag 'jquery.jgrowl.js', 'closet_hangers/index' + diff --git a/public/javascripts/closet_hangers/index.js b/public/javascripts/closet_hangers/index.js new file mode 100644 index 00000000..a4574dc3 --- /dev/null +++ b/public/javascripts/closet_hangers/index.js @@ -0,0 +1,70 @@ +(function () { + var hangersEl = $('#closet-hangers.current-user'); + hangersEl.addClass('js'); + + $.fn.hasChanged = function () { + return this.data('previousValue') != this.val(); + } + + $.fn.revertValue = function () { + this.each(function () { + var el = $(this); + el.val(el.data('previousValue')); + }); + } + + $.fn.storeValue = function () { + this.each(function () { + var el = $(this); + el.data('previousValue', el.val()); + }); + } + + function submitForm(form) { + if(form.data('loading')) return false; + var input = form.children("input[type=number]"); + if(input.hasChanged()) { + var objectWrapper = form.closest(".object").addClass("loading"); + var span = objectWrapper.find("span").text(input.val()); + form.data('loading', true); + + $.ajax({ + url: form.attr("action") + ".json", + type: "post", + data: form.serialize(), + dataType: "json", + complete: function (data) { + objectWrapper.removeClass("loading"); + form.data('loading', false); + }, + success: function () { + input.storeValue(); + }, + error: function (xhr) { + var data = $.parseJSON(xhr.responseText); + input.revertValue(); + span.text(input.val()); + if(typeof data.errors != 'undefined') { + $.jGrowl("Error updating quantity: " + data.errors.join(", ")); + } else { + $.jGrowl("We had trouble updating the quantity just now. Try again?"); + } + } + }); + } + } + + hangersEl.find('form').submit(function (e) { + e.preventDefault(); + submitForm($(this)); + }); + + hangersEl.find('input[type=number]').change(function () { + submitForm($(this).parent()); + }).storeValue(); + + hangersEl.find('div.object').mouseleave(function () { + submitForm($(this).find('form')); + }); +})(); + diff --git a/public/stylesheets/compiled/screen.css b/public/stylesheets/compiled/screen.css index d075cabd..5609ad53 100644 --- a/public/stylesheets/compiled/screen.css +++ b/public/stylesheets/compiled/screen.css @@ -271,8 +271,8 @@ ul.buttons li, ul.buttons li form { vertical-align: middle; *display: inline; *vertical-align: auto; - margin: 0.5em 0; - padding: 0 0.5em; + margin: 8px 0; + padding: 0 8px; position: relative; text-align: center; vertical-align: top; @@ -323,11 +323,11 @@ ul.buttons li, ul.buttons li form { } /* line 190, ../../../app/stylesheets/_layout.sass */ .object .nc-icon { - right: 16px; + right: 18px; } /* line 193, ../../../app/stylesheets/_layout.sass */ .object .closeted-icon { - left: 16px; + left: 18px; } /* line 196, ../../../app/stylesheets/_layout.sass */ @@ -610,15 +610,16 @@ body.closet_hangers-index #closet-hangers .object .quantity { background: white; padding: 2px 4px; position: absolute; - left: 16px; + left: 18px; + line-height: 1; text-align: left; - top: 56px; + top: 60px; } -/* line 30, ../../../app/stylesheets/closet_hangers/_index.sass */ +/* line 31, ../../../app/stylesheets/closet_hangers/_index.sass */ body.closet_hangers-index #closet-hangers .object .quantity form { display: none; } -/* line 33, ../../../app/stylesheets/closet_hangers/_index.sass */ +/* line 34, ../../../app/stylesheets/closet_hangers/_index.sass */ body.closet_hangers-index #closet-hangers .object .quantity span, body.closet_hangers-index #closet-hangers .object .quantity input[type=number] { font-size: 16px; font-weight: bold; @@ -630,7 +631,7 @@ body.closet_hangers-index #closet-hangers.current-user .object:hover .quantity { -o-opacity: 1; -khtml-opacity: 1; background: transparent; - top: 55px; + top: 56px; padding: 0; } /* line 45, ../../../app/stylesheets/closet_hangers/_index.sass */ @@ -642,7 +643,7 @@ body.closet_hangers-index #closet-hangers.current-user .object:hover .quantity f display: inline; } /* line 51, ../../../app/stylesheets/closet_hangers/_index.sass */ -body.closet_hangers-index #closet-hangers.current-user .object:hover .quantity form input[type=number] { +body.closet_hangers-index #closet-hangers.current-user .object:hover .quantity input[type=number] { padding: 2px; width: 2em; } @@ -650,6 +651,23 @@ body.closet_hangers-index #closet-hangers.current-user .object:hover .quantity f body.closet_hangers-index #closet-hangers.current-user .object:hover .quantity input[type=submit] { font-size: 85%; } +/* line 60, ../../../app/stylesheets/closet_hangers/_index.sass */ +body.closet_hangers-index #closet-hangers.current-user.js .object:hover .quantity input[type=number] { + width: 2.5em; +} +/* line 63, ../../../app/stylesheets/closet_hangers/_index.sass */ +body.closet_hangers-index #closet-hangers.current-user.js .object:hover .quantity input[type=submit] { + display: none; +} +/* line 66, ../../../app/stylesheets/closet_hangers/_index.sass */ +body.closet_hangers-index #closet-hangers.current-user.js .object.loading { + background: #eeffee; + outline: 1px solid #006600; +} +/* line 70, ../../../app/stylesheets/closet_hangers/_index.sass */ +body.closet_hangers-index #closet-hangers.current-user.js .object.loading .quantity span:after { + content: "…"; +} /* line 3, ../../../app/stylesheets/closet_pages/_new.sass */ body.closet_pages-new #closet-page-form, body.closet_pages-create #closet-page-form {