nice page to view current user's outfits

This commit is contained in:
Emi Matchu 2011-03-23 18:23:01 -04:00
parent 88fbc72a46
commit 1207e84804
14 changed files with 663 additions and 514 deletions

View file

@ -1,6 +1,6 @@
class OutfitsController < ApplicationController class OutfitsController < ApplicationController
before_filter :find_authorized_outfit, :only => [:update, :destroy] before_filter :find_authorized_outfit, :only => [:update, :destroy]
def create def create
@outfit = Outfit.build_for_user(current_user, params[:outfit]) @outfit = Outfit.build_for_user(current_user, params[:outfit])
if @outfit.save if @outfit.save
@ -9,12 +9,22 @@ class OutfitsController < ApplicationController
render_outfit_errors render_outfit_errors
end end
end end
def for_current_user def index
@outfits = user_signed_in? ? current_user.outfits : [] if user_signed_in?
render :json => @outfits @outfits = current_user.outfits.wardrobe_order
respond_to do |format|
format.html { render }
format.json { render :json => @outfits }
end
else
respond_to do |format|
format.html { redirect_to login_path(:return_to => request.fullpath) }
format.json { render :json => [] }
end
end
end end
def destroy def destroy
if @outfit.destroy if @outfit.destroy
render :json => true render :json => true
@ -22,7 +32,7 @@ class OutfitsController < ApplicationController
render :json => false, :status => :bad_request render :json => false, :status => :bad_request
end end
end end
def new def new
unless fragment_exist?(:action_suffix => 'start_from_scratch_form_content') unless fragment_exist?(:action_suffix => 'start_from_scratch_form_content')
@colors = Color.all_ordered_by_name @colors = Color.all_ordered_by_name
@ -32,7 +42,7 @@ class OutfitsController < ApplicationController
@top_contributors = User.top_contributors.limit(User::PreviewTopContributorsCount) @top_contributors = User.top_contributors.limit(User::PreviewTopContributorsCount)
end end
end end
def show def show
@outfit = Outfit.find(params[:id]) @outfit = Outfit.find(params[:id])
respond_to do |format| respond_to do |format|
@ -40,7 +50,7 @@ class OutfitsController < ApplicationController
format.json { render :json => @outfit } format.json { render :json => @outfit }
end end
end end
def update def update
if @outfit.update_attributes(params[:outfit]) if @outfit.update_attributes(params[:outfit])
render :json => true render :json => true
@ -48,15 +58,16 @@ class OutfitsController < ApplicationController
render_outfit_errors render_outfit_errors
end end
end end
private private
def find_authorized_outfit def find_authorized_outfit
raise ActiveRecord::RecordNotFound unless user_signed_in? raise ActiveRecord::RecordNotFound unless user_signed_in?
@outfit = current_user.outfits.find(params[:id]) @outfit = current_user.outfits.find(params[:id])
end end
def render_outfit_errors def render_outfit_errors
render :json => {:errors => @outfit.errors}, :status => :bad_request render :json => {:errors => @outfit.errors}, :status => :bad_request
end end
end end

View file

@ -2,14 +2,34 @@ module OutfitsHelper
def destination_tag(value) def destination_tag(value)
hidden_field_tag 'destination', value, :id => nil hidden_field_tag 'destination', value, :id => nil
end end
def link_to_edit_outfit(content_or_outfit, outfit_or_options, options={})
if block_given?
content = capture_haml(&Proc.new)
outfit = content_or_outfit
options = outfit_or_options
else
content = content_or_outfit
outfit = outfit_or_options
end
query = outfit.to_query
query << "&outfit=#{outfit.id}" if outfit.user == current_user
link_to content, wardrobe_path(:anchor => query), options
end
def outfit_li_for(outfit)
class_name = outfit.starred? ? 'starred' : nil
content_tag :li, :class => class_name, &Proc.new
end
def pet_attribute_select(name, collection, value=nil) def pet_attribute_select(name, collection, value=nil)
select_tag name, select_tag name,
options_from_collection_for_select(collection, :id, :human_name, value) options_from_collection_for_select(collection, :id, :human_name, value)
end end
def pet_name_tag(options={}) def pet_name_tag(options={})
options = {:spellcheck => false, :id => nil}.merge(options) options = {:spellcheck => false, :id => nil}.merge(options)
text_field_tag 'name', nil, options text_field_tag 'name', nil, options
end end
end end

View file

@ -5,29 +5,31 @@ class Outfit < ActiveRecord::Base
has_many :worn_items, :through => :worn_item_outfit_relationships, :source => :item has_many :worn_items, :through => :worn_item_outfit_relationships, :source => :item
belongs_to :pet_state belongs_to :pet_state
belongs_to :user belongs_to :user
validates :name, :presence => {:if => :user_id}, :uniqueness => {:scope => :user_id, :if => :user_id} validates :name, :presence => {:if => :user_id}, :uniqueness => {:scope => :user_id, :if => :user_id}
validates :pet_state, :presence => true validates :pet_state, :presence => true
attr_accessible :name, :pet_state_id, :starred, :worn_and_unworn_item_ids attr_accessible :name, :pet_state_id, :starred, :worn_and_unworn_item_ids
scope :wardrobe_order, order('starred DESC', :name)
def as_json(more_options={}) def as_json(more_options={})
serializable_hash :only => [:id, :name, :pet_state_id, :starred], serializable_hash :only => [:id, :name, :pet_state_id, :starred],
:methods => [:color_id, :species_id, :worn_and_unworn_item_ids] :methods => [:color_id, :species_id, :worn_and_unworn_item_ids]
end end
def closet_item_ids def closet_item_ids
item_outfit_relationships.map(&:item_id) item_outfit_relationships.map(&:item_id)
end end
def color_id def color_id
pet_state.pet_type.color_id pet_state.pet_type.color_id
end end
def species_id def species_id
pet_state.pet_type.species_id pet_state.pet_type.species_id
end end
def to_query def to_query
{ {
:closet => closet_item_ids, :closet => closet_item_ids,
@ -37,7 +39,7 @@ class Outfit < ActiveRecord::Base
:state => pet_state_id :state => pet_state_id
}.to_query }.to_query
end end
def worn_and_unworn_item_ids def worn_and_unworn_item_ids
{:worn => [], :unworn => []}.tap do |output| {:worn => [], :unworn => []}.tap do |output|
item_outfit_relationships.each do |rel| item_outfit_relationships.each do |rel|
@ -46,7 +48,7 @@ class Outfit < ActiveRecord::Base
end end
end end
end end
def worn_and_unworn_item_ids=(all_item_ids) def worn_and_unworn_item_ids=(all_item_ids)
new_rels = [] new_rels = []
all_item_ids.each do |key, item_ids| all_item_ids.each do |key, item_ids|
@ -62,11 +64,11 @@ class Outfit < ActiveRecord::Base
end end
self.item_outfit_relationships = new_rels self.item_outfit_relationships = new_rels
end end
def worn_item_ids def worn_item_ids
worn_and_unworn_item_ids[:worn] worn_and_unworn_item_ids[:worn]
end end
def self.build_for_user(user, params) def self.build_for_user(user, params)
Outfit.new.tap do |outfit| Outfit.new.tap do |outfit|
name = params.delete(:name) name = params.delete(:name)
@ -80,3 +82,4 @@ class Outfit < ActiveRecord::Base
end end
end end
end end

View file

@ -1,5 +1,8 @@
@import ../shared/jquery.jgrowl @import ../shared/jquery.jgrowl
@import icon
@import star
$object-padding: 6px $object-padding: 6px
$nc-icon-size: 16px $nc-icon-size: 16px
@ -16,14 +19,8 @@ $outfit-header-padding: 24px
$outfit-content-width: $sidebar-unit-inner-width - $outfit-thumbnail-size - $outfit-thumbnail-margin - 32px $outfit-content-width: $sidebar-unit-inner-width - $outfit-thumbnail-size - $outfit-thumbnail-margin - 32px
$outfit-content-inner-width: $outfit-content-width - $outfit-header-padding $outfit-content-inner-width: $outfit-content-width - $outfit-header-padding
=icon
bottom: -2px
height: 16px
position: relative
width: 16px
=outfit =outfit
//+clearfix +outfit-star-shifted
padding: .25em 0 padding: .25em 0
//.outfit-thumbnail //.outfit-thumbnail
float: left float: left
@ -38,9 +35,6 @@ $outfit-content-inner-width: $outfit-content-width - $outfit-header-padding
position: absolute position: absolute
top: -$outfit-thumbnail-original-size / 4 top: -$outfit-thumbnail-original-size / 4
width: $outfit-thumbnail-original-size width: $outfit-thumbnail-original-size
//> div
float: left
width: $outfit-content-width
.outfit-delete .outfit-delete
+reset-awesome-button +reset-awesome-button
+opacity(.5) +opacity(.5)
@ -55,23 +49,6 @@ $outfit-content-inner-width: $outfit-content-width - $outfit-header-padding
header header
display: block display: block
padding-left: $outfit-header-padding padding-left: $outfit-header-padding
.outfit-star
+icon
background:
image: image-url("unstarred.png")
position: left top
repeat: no-repeat
cursor: pointer
display: block
float: left
margin-left: -24px
/* makes it not take up inline space
&.starred .outfit-star
background-image: image-url("star.png")
&.loading .outfit-star
background-image: image-url("loading.gif")
&.loading.active .outfit-star
background-image: image-url("loading_current_outfit.gif")
h4 h4
cursor: pointer cursor: pointer
display: inline display: inline
@ -208,7 +185,7 @@ body.outfits-edit
display: block display: block
.sidebar-view .sidebar-view
h2 h2
margin: margin:
bottom: .25em bottom: .25em
left: $sidebar-unit-horizontal-padding left: $sidebar-unit-horizontal-padding
#preview-closet #preview-closet
@ -413,24 +390,24 @@ body.outfits-edit
margin: margin:
right: $sidebar-unit-horizontal-padding right: $sidebar-unit-horizontal-padding
top: 1em top: 1em
#save-success, #save-error, #outfit-not-found #save-success, #save-error, #outfit-not-found
+sidebar-view-child +sidebar-view-child
display: none display: none
margin: margin:
top: 1em top: 1em
text-align: center text-align: center
#save-success #save-success
+notice +notice
#save-error, #outfit-not-found #save-error, #outfit-not-found
+error +error
#userbar-message #userbar-message
+opacity(.5) +opacity(.5)
display: none display: none
#new-outfit #new-outfit
+outfit +outfit
+sidebar-view-child +sidebar-view-child
@ -441,18 +418,18 @@ body.outfits-edit
text-decoration: none text-decoration: none
.outfit-star .outfit-star
margin-top: .5em margin-top: .5em
#new-outfit-name #new-outfit-name
font: inherit font: inherit
line-height: 1 line-height: 1
#preview-saving-outfit #preview-saving-outfit
display: none display: none
padding-bottom: 1em padding-bottom: 1em
#pet-type-form, #pet-state-form, #preview-swf, #preview-search-form #pet-type-form, #pet-state-form, #preview-swf, #preview-search-form
position: relative position: relative
.control-overlay .control-overlay
height: 100% height: 100%
left: 0 left: 0
@ -460,27 +437,27 @@ body.outfits-edit
top: 0 top: 0
width: 100% width: 100%
z-index: 5 z-index: 5
#preview-sidebar-nav-outfits, #save-outfit-signed-in #preview-sidebar-nav-outfits, #save-outfit-signed-in
display: none display: none
form#save-outfit-form form#save-outfit-form
+outfit +outfit
display: none display: none
margin-right: 0 margin-right: 0
padding: 0 padding: 0
.outfit-star, input, button .outfit-star, input, button
+inline-block +inline-block
float: none float: none
vertical-align: top vertical-align: top
.outfit-star .outfit-star
margin-top: .25em margin-top: .25em
.outfit-url .outfit-url
font-size: 75% font-size: 75%
&.user-signed-in &.user-signed-in
#preview-sidebar-nav-outfits #preview-sidebar-nav-outfits
display: block display: block
@ -499,10 +476,11 @@ body.outfits-edit
display: block display: block
#save-outfit, #save-current-outfit, #save-outfit-copy, #current-outfit-permalink #save-outfit, #save-current-outfit, #save-outfit-copy, #current-outfit-permalink
display: none display: none
&.user-not-signed-in &.user-not-signed-in
#share-outfit, #save-outfit-not-signed-in #share-outfit, #save-outfit-not-signed-in
display: inline-block display: inline-block
#save-outfit-wrapper.shared-outfit #save-outfit-wrapper.shared-outfit
#current-outfit-permalink, #current-outfit-url #current-outfit-permalink, #current-outfit-url
display: inline-block display: inline-block

View file

@ -0,0 +1,9 @@
$icon-width: 16px
$icon-height: 16px
=icon
bottom: -2px
height: $icon-height
position: relative
width: $icon-width

View file

@ -0,0 +1,16 @@
@import star
body.outfits-index
#outfits
list-style: none
li
+outfit-star
h4
display: inline
.outfit-edit-link
font-size: 85%
margin-left: 1em

View file

@ -0,0 +1,27 @@
@import icon
=outfit-star
.outfit-star
+icon
background:
image: image-url("unstarred.png")
position: left top
repeat: no-repeat
cursor: pointer
display: block
float: left
margin-right: $icon-width / 2
&.starred .outfit-star
background-image: image-url("star.png")
&.loading .outfit-star
background-image: image-url("loading.gif")
&.loading.active .outfit-star
background-image: image-url("loading_current_outfit.gif")
=outfit-star-shifted
+outfit-star
.outfit-star
margin-left: -$icon-width * 1.5
margin-right: 0

View file

@ -11,8 +11,10 @@
@import items/index @import items/index
@import items/show @import items/show
@import outfits/edit @import outfits/edit
@import outfits/index
@import outfits/new @import outfits/new
@import outfits/show @import outfits/show
@import pets/bulk @import pets/bulk
@import static/terms @import static/terms
@import users/top_contributors @import users/top_contributors

View file

@ -24,7 +24,7 @@
= yield(:content) = yield(:content)
- else - else
= yield = yield
- if home_link? - if home_link?
%a#home-link{:href => root_path} %a#home-link{:href => root_path}
%span Dress to Impress %span Dress to Impress
@ -34,6 +34,7 @@
%span %span
== Hey, #{link_to current_user.name, user_contributions_path(current_user)}! == Hey, #{link_to current_user.name, user_contributions_path(current_user)}!
== You have #{current_user.points} points. == You have #{current_user.points} points.
= link_to 'Outfits', current_user_outfits_path
= link_to 'Settings', Openneo::Auth.remote_settings_url = link_to 'Settings', Openneo::Auth.remote_settings_url
= link_to 'Log out', logout_path_with_return_to = link_to 'Log out', logout_path_with_return_to
- else - else
@ -64,3 +65,4 @@
Images &copy; 2000-2010 Neopets, Inc. All Rights Reserved. Images &copy; 2000-2010 Neopets, Inc. All Rights Reserved.
Used With Permission Used With Permission
= yield(:javascripts) = yield(:javascripts)

View file

@ -0,0 +1,5 @@
= outfit_li_for(outfit) do
.outfit-star
%h4= link_to outfit.name, outfit
= link_to_edit_outfit '(edit)', outfit, :class => 'outfit-edit-link'

View file

@ -0,0 +1,20 @@
- title 'Your outfits'
%p
These are the outfits that you've saved to Dress to Impress so far. To save
some more, head to the wardrobe and click Save Outfit in the top right
corner.
%p
The link for each outfit is totally public, so please feel free to share the
URL with the whole wide world.
- unless @outfits.empty?
%ul#outfits= render @outfits
- else
%p
You haven't saved any outfits yet.
- succeed ',' do
= link_to 'Start at the home page', root_path
enter a pet name or choose a color combination, create the outfit of your
dreams, and click "Save Outfit" in the top right corner.
%p
It'll be fantastic. Promise.

View file

@ -1,6 +1,8 @@
- title(@outfit.name || "Shared outfit") - title(@outfit.name || "Shared outfit")
%a.button#outfit-wardrobe-link{:href => wardrobe_path(:anchor => @outfit.to_query)} = link_to_edit_outfit(@outfit, :class => 'button', :id => 'outfit-wardrobe-link') do
Edit a copy Edit
- unless @outfit.user == current_user
a copy
- if @outfit.user_id - if @outfit.user_id
#outfit-user #outfit-user
Created by Created by
@ -15,3 +17,4 @@
var INITIAL_OUTFIT_DATA = #{@outfit.to_json}; var INITIAL_OUTFIT_DATA = #{@outfit.to_json};
= include_javascript_libraries :jquery, :swfobject = include_javascript_libraries :jquery, :swfobject
= include_javascripts :show_outfit_package = include_javascripts :show_outfit_package

View file

@ -1,19 +1,19 @@
OpenneoImpressItems::Application.routes.draw do |map| OpenneoImpressItems::Application.routes.draw do |map|
root :to => 'outfits#new' root :to => 'outfits#new'
devise_for :users devise_for :users
match '/item_zone_sets.json' => 'ItemZoneSets#index' match '/item_zone_sets.json' => 'ItemZoneSets#index'
match '/bodies/:body_id/swf_assets.json' => 'swf_assets#index', :as => :body_swf_assets match '/bodies/:body_id/swf_assets.json' => 'swf_assets#index', :as => :body_swf_assets
match '/items/:item_id/swf_assets.json' => 'swf_assets#index', :as => :item_swf_assets match '/items/:item_id/swf_assets.json' => 'swf_assets#index', :as => :item_swf_assets
match '/items/:item_id/bodies/:body_id/swf_assets.json' => 'swf_assets#index', :as => :item_swf_assets_for_body_id match '/items/:item_id/bodies/:body_id/swf_assets.json' => 'swf_assets#index', :as => :item_swf_assets_for_body_id
match '/pet_types/:pet_type_id/swf_assets.json' => 'swf_assets#index', :as => :pet_type_swf_assets match '/pet_types/:pet_type_id/swf_assets.json' => 'swf_assets#index', :as => :pet_type_swf_assets
match '/pet_states/:pet_state_id/swf_assets.json' => 'swf_assets#index', :as => :pet_state_swf_assets match '/pet_states/:pet_state_id/swf_assets.json' => 'swf_assets#index', :as => :pet_state_swf_assets
match '/species/:species_id/color/:color_id/pet_type.json' => 'pet_types#show' match '/species/:species_id/color/:color_id/pet_type.json' => 'pet_types#show'
match '/roulette' => 'roulettes#new', :as => :roulette match '/roulette' => 'roulettes#new', :as => :roulette
resources :contributions, :only => [:index] resources :contributions, :only => [:index]
resources :items, :only => [:index, :show] do resources :items, :only => [:index, :show] do
collection do collection do
@ -22,23 +22,24 @@ OpenneoImpressItems::Application.routes.draw do |map|
end end
resources :outfits, :only => [:show, :create, :update, :destroy] resources :outfits, :only => [:show, :create, :update, :destroy]
resources :pet_attributes, :only => [:index] resources :pet_attributes, :only => [:index]
match '/users/current-user/outfits.json' => 'outfits#for_current_user' match '/users/current-user/outfits' => 'outfits#index', :as => :current_user_outfits
match '/pets/load' => 'pets#load', :method => :post, :as => :load_pet match '/pets/load' => 'pets#load', :method => :post, :as => :load_pet
match '/pets/bulk' => 'pets#bulk', :as => :bulk_pets match '/pets/bulk' => 'pets#bulk', :as => :bulk_pets
match '/login' => 'sessions#new', :as => :login match '/login' => 'sessions#new', :as => :login
match '/logout' => 'sessions#destroy', :as => :logout match '/logout' => 'sessions#destroy', :as => :logout
match '/users/authorize' => 'sessions#create' match '/users/authorize' => 'sessions#create'
resources :user, :only => [] do resources :user, :only => [] do
resources :contributions, :only => [:index] resources :contributions, :only => [:index]
end end
match 'users/top-contributors' => 'users#top_contributors', :as => :top_contributors match 'users/top-contributors' => 'users#top_contributors', :as => :top_contributors
match 'users/top_contributors' => redirect('/users/top-contributors') match 'users/top_contributors' => redirect('/users/top-contributors')
match '/wardrobe' => 'outfits#edit', :as => :wardrobe match '/wardrobe' => 'outfits#edit', :as => :wardrobe
match '/terms' => 'static#terms', :as => :terms match '/terms' => 'static#terms', :as => :terms
end end

File diff suppressed because it is too large Load diff