Compare commits
10 commits
2bd8afd486
...
4bcc3aaebb
Author | SHA1 | Date | |
---|---|---|---|
4bcc3aaebb | |||
5890e52e53 | |||
dd8426fefd | |||
2a9818b2d1 | |||
0b72b5568c | |||
86e1f31231 | |||
a99fb3ec02 | |||
d11c18129d | |||
0958111341 | |||
775baa250b |
21 changed files with 220 additions and 99 deletions
|
@ -74,7 +74,7 @@ $container_width: 800px
|
|||
input, button, select, label
|
||||
cursor: pointer
|
||||
|
||||
input[type=text], input[type=password], input[type=search], input[type=number], input[type=email], select, textarea
|
||||
input[type=text], input[type=password], input[type=search], input[type=number], input[type=email], input[type=url], select, textarea
|
||||
border-radius: 3px
|
||||
background: #fff
|
||||
border: 1px solid $input-border-color
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
body.alt_styles-index
|
||||
.alt-styles-header
|
||||
margin-top: 1em
|
||||
margin-bottom: .5em
|
||||
|
||||
.alt-styles-list
|
||||
list-style: none
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
gap: 1.5em
|
||||
|
||||
.alt-style
|
||||
text-align: center
|
||||
width: 80px
|
||||
|
||||
.alt-style-thumbnail
|
||||
width: 80px
|
||||
height: 80px
|
43
app/assets/stylesheets/alt_styles/edit.sass
Normal file
43
app/assets/stylesheets/alt_styles/edit.sass
Normal file
|
@ -0,0 +1,43 @@
|
|||
.alt-style-preview
|
||||
width: 300px
|
||||
height: 300px
|
||||
margin: 0 auto
|
||||
|
||||
.alt-style-form
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 1em
|
||||
align-items: flex-start
|
||||
|
||||
fieldset
|
||||
width: 100%
|
||||
display: grid
|
||||
grid-template-columns: auto 1fr
|
||||
align-items: center
|
||||
gap: 1em
|
||||
|
||||
> *:nth-child(2n)
|
||||
width: 40rch
|
||||
max-width: 100%
|
||||
box-sizing: border-box
|
||||
|
||||
input[type=url]
|
||||
font-size: .85em
|
||||
|
||||
label
|
||||
font-weight: bold
|
||||
|
||||
.thumbnail-field
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: .25em
|
||||
|
||||
img
|
||||
width: 40px
|
||||
height: 40px
|
||||
|
||||
input
|
||||
flex: 1 0 20ch
|
||||
|
||||
.field_with_errors
|
||||
display: contents
|
3
app/assets/stylesheets/alt_styles/index.sass
Normal file
3
app/assets/stylesheets/alt_styles/index.sass
Normal file
|
@ -0,0 +1,3 @@
|
|||
.rainbow-pool-list
|
||||
.name span
|
||||
display: inline-block
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
@import partials/jquery.jgrowl
|
||||
|
||||
@import alt_styles/index
|
||||
@import closet_hangers/index
|
||||
@import closet_lists/form
|
||||
@import neopets_page_import_tasks/new
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@import "../partials/clean/constants"
|
||||
|
||||
.pet-filters
|
||||
.rainbow-pool-filters
|
||||
fieldset
|
||||
display: flex
|
||||
flex-direction: row
|
||||
|
@ -12,19 +12,20 @@
|
|||
display: contents
|
||||
font-weight: bold
|
||||
|
||||
[role=navigation]
|
||||
margin-block: .5em
|
||||
text-align: center
|
||||
select
|
||||
width: 16ch
|
||||
|
||||
.pet-types
|
||||
.rainbow-pool-list
|
||||
list-style-type: none
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
justify-content: center
|
||||
gap: .5em
|
||||
|
||||
--preview-base-width: 150px
|
||||
|
||||
> li
|
||||
width: 150px
|
||||
width: var(--preview-base-width)
|
||||
max-width: calc(50% - .25em)
|
||||
min-width: 150px
|
||||
box-sizing: border-box
|
||||
|
@ -40,7 +41,7 @@
|
|||
outline: 1px solid $module-border-color
|
||||
background: $module-bg-color
|
||||
|
||||
img
|
||||
.preview
|
||||
width: 100%
|
||||
height: auto
|
||||
aspect-ratio: 1 / 1
|
||||
|
@ -53,3 +54,9 @@
|
|||
margin: 0 auto
|
||||
position: relative
|
||||
z-index: 1
|
||||
|
||||
.rainbow-pool-pagination
|
||||
margin-block: .5em
|
||||
display: flex
|
||||
justify-content: center
|
||||
gap: 1em
|
|
@ -1,43 +1,7 @@
|
|||
@import "../partials/clean/constants"
|
||||
|
||||
.pet-states
|
||||
list-style-type: none
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
justify-content: center
|
||||
gap: .5em
|
||||
|
||||
> li
|
||||
width: 200px
|
||||
max-width: calc(50% - .25em)
|
||||
min-width: 150px
|
||||
box-sizing: border-box
|
||||
text-align: center
|
||||
|
||||
a
|
||||
display: block
|
||||
border-radius: 1em
|
||||
padding: .5em
|
||||
background: white
|
||||
text-decoration: none
|
||||
&:hover
|
||||
outline: 1px solid $module-border-color
|
||||
background: $module-bg-color
|
||||
|
||||
outfit-viewer
|
||||
width: 100%
|
||||
height: auto
|
||||
aspect-ratio: 1 / 1
|
||||
position: relative
|
||||
z-index: 0
|
||||
margin-bottom: -1em
|
||||
|
||||
.name
|
||||
background: inherit
|
||||
padding: .25em .5em
|
||||
border-radius: .5em
|
||||
position: relative
|
||||
z-index: 1
|
||||
.rainbow-pool-list
|
||||
--preview-base-width: 200px
|
||||
|
||||
.glitched
|
||||
cursor: help
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
class AltStylesController < ApplicationController
|
||||
before_action :support_staff_only, except: [:index]
|
||||
|
||||
def index
|
||||
@alt_styles = AltStyle.includes(:species, :color, :swf_assets).
|
||||
order(:species_id, :color_id)
|
||||
@all_alt_styles = AltStyle.includes(:species, :color)
|
||||
|
||||
if params[:species_id]
|
||||
@species = Species.find(params[:species_id])
|
||||
@alt_styles = @alt_styles.merge(@species.alt_styles)
|
||||
end
|
||||
@all_colors = @all_alt_styles.map(&:color).uniq.sort_by(&:name)
|
||||
@all_species = @all_alt_styles.map(&:species).uniq.sort_by(&:name)
|
||||
|
||||
# We're going to link to the HTML5 image URL, so make sure we have all the
|
||||
@all_series_names = @all_alt_styles.map(&:series_name).uniq.sort
|
||||
@all_color_names = @all_colors.map(&:human_name)
|
||||
@all_species_names = @all_species.map(&:human_name)
|
||||
|
||||
@series_name = params[:series]
|
||||
@color = find_color
|
||||
@species = find_species
|
||||
|
||||
@alt_styles = @all_alt_styles.includes(:swf_assets).
|
||||
by_creation_date.order(:color_id, :species_id, :series_name).
|
||||
paginate(page: params[:page], per_page: 30)
|
||||
@alt_styles.where!(series_name: @series_name) if @series_name.present?
|
||||
@alt_styles.merge!(@color.alt_styles) if @color
|
||||
@alt_styles.merge!(@species.alt_styles) if @species
|
||||
|
||||
# We're using the HTML5 image for our preview, so make sure we have all the
|
||||
# manifests ready!
|
||||
SwfAsset.preload_manifests @alt_styles.map(&:swf_assets).flatten
|
||||
|
||||
|
@ -30,4 +44,39 @@ class AltStylesController < ApplicationController
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@alt_style = AltStyle.find params[:id]
|
||||
end
|
||||
|
||||
def update
|
||||
@alt_style = AltStyle.find params[:id]
|
||||
|
||||
if @alt_style.update(alt_style_params)
|
||||
flash[:notice] = "\"#{@alt_style.full_name}\" successfully saved!"
|
||||
redirect_to alt_styles_path
|
||||
else
|
||||
render action: :edit, status: :bad_request
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def alt_style_params
|
||||
params.require(:alt_style).permit(:series_name, :thumbnail_url)
|
||||
end
|
||||
|
||||
def find_color
|
||||
if params[:color]
|
||||
Color.find_by(name: params[:color])
|
||||
end
|
||||
end
|
||||
|
||||
def find_species
|
||||
if params[:species_id]
|
||||
Species.find_by(id: params[:species_id])
|
||||
elsif params[:species]
|
||||
Species.find_by(name: params[:species])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
9
app/helpers/alt_styles_helper.rb
Normal file
9
app/helpers/alt_styles_helper.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
module AltStylesHelper
|
||||
def view_or_edit_alt_style_url(alt_style)
|
||||
if support_staff?
|
||||
edit_alt_style_path alt_style
|
||||
else
|
||||
alt_style.preview_image_url
|
||||
end
|
||||
end
|
||||
end
|
|
@ -70,16 +70,11 @@ module OutfitsHelper
|
|||
text_field_tag 'name', nil, options
|
||||
end
|
||||
|
||||
def outfit_viewer(outfit_or_options)
|
||||
outfit = if outfit_or_options.is_a? Hash
|
||||
Outfit.new(outfit_or_options)
|
||||
elsif outfit_or_options.is_a? Outfit
|
||||
outfit_or_options
|
||||
else
|
||||
raise TypeError, "must be an outfit or hash of options to create one"
|
||||
end
|
||||
def outfit_viewer(outfit=nil, pet_state: nil, **html_options)
|
||||
outfit = Outfit.new(pet_state:) if outfit.nil? && pet_state.present?
|
||||
raise "outfit_viewer must have outfit or pet state" if outfit.nil?
|
||||
|
||||
render partial: "outfit_viewer", locals: {outfit:}
|
||||
render partial: "outfit_viewer", locals: {outfit:, html_options:}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ class AltStyle < ApplicationRecord
|
|||
has_many :contributions, as: :contributed, inverse_of: :contributed
|
||||
|
||||
validates :body_id, presence: true
|
||||
validates :series_name, presence: true, allow_nil: true
|
||||
validates :thumbnail_url, presence: true
|
||||
|
||||
before_create :infer_series_name
|
||||
before_create :infer_thumbnail_url
|
||||
|
@ -18,12 +20,17 @@ class AltStyle < ApplicationRecord
|
|||
species = Species.find_by_name!(species_name)
|
||||
where(series_name:, color_id: color.id, species_id: species.id)
|
||||
}
|
||||
scope :by_creation_date, -> {
|
||||
order("DATE(created_at) DESC")
|
||||
}
|
||||
|
||||
def name
|
||||
def pet_name
|
||||
I18n.translate('pet_types.human_name', color_human_name: color.human_name,
|
||||
species_human_name: species.human_name)
|
||||
end
|
||||
|
||||
alias_method :name, :pet_name
|
||||
|
||||
# If the series_name hasn't yet been set manually by support staff, show the
|
||||
# string "<New?>" instead. But it won't be searchable by that string—that is,
|
||||
# `fits:<New?>-faerie-draik` intentionally will not work, and the canonical
|
||||
|
@ -42,6 +49,10 @@ class AltStyle < ApplicationRecord
|
|||
"#{series_name} #{color.human_name}"
|
||||
end
|
||||
|
||||
def full_name
|
||||
"#{series_name} #{name}"
|
||||
end
|
||||
|
||||
def preview_image_url
|
||||
swf_asset = swf_assets.first
|
||||
return nil if swf_asset.nil?
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class Color < ApplicationRecord
|
||||
has_many :pet_types
|
||||
has_many :alt_styles
|
||||
|
||||
scope :alphabetical, -> { order(:name) }
|
||||
scope :basic, -> { where(basic: true) }
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
%li.alt-style
|
||||
= link_to alt_style.preview_image_url do
|
||||
= image_tag alt_style.thumbnail_url, class: 'alt-style-thumbnail'
|
||||
.alt-style-name= alt_style.name
|
||||
%li
|
||||
= link_to view_or_edit_alt_style_url(alt_style) do
|
||||
= image_tag alt_style.preview_image_url, class: "preview", loading: "lazy"
|
||||
.name
|
||||
%span= alt_style.series_name
|
||||
%span= alt_style.pet_name
|
35
app/views/alt_styles/edit.html.haml
Normal file
35
app/views/alt_styles/edit.html.haml
Normal file
|
@ -0,0 +1,35 @@
|
|||
- title @alt_style.full_name
|
||||
- use_responsive_design
|
||||
|
||||
%ol.breadcrumbs
|
||||
%li= link_to "Alt Styles", alt_styles_path
|
||||
%li
|
||||
= link_to @alt_style.color.human_name,
|
||||
alt_styles_path(color: @alt_style.color.human_name)
|
||||
%li{"data-relation-to-prev": "sibling"}
|
||||
= link_to @alt_style.species.human_name,
|
||||
alt_styles_path(species: @alt_style.species.human_name)
|
||||
%li= @alt_style.series_name
|
||||
|
||||
= image_tag @alt_style.preview_image_url, class: "alt-style-preview"
|
||||
|
||||
= form_with model: @alt_style, class: "alt-style-form" do |f|
|
||||
- if @alt_style.errors.any?
|
||||
%p
|
||||
Could not save:
|
||||
%ul.errors
|
||||
- @alt_style.errors.each do |error|
|
||||
%li= error.full_message
|
||||
%fieldset
|
||||
= f.label :series_name, "Series"
|
||||
= f.text_field :series_name
|
||||
= f.label :thumbnail_url, "Thumbnail"
|
||||
.thumbnail-field
|
||||
- if @alt_style.thumbnail_url?
|
||||
= image_tag @alt_style.thumbnail_url
|
||||
= f.url_field :thumbnail_url
|
||||
= f.submit "Save changes"
|
||||
|
||||
- content_for :stylesheets do
|
||||
= stylesheet_link_tag "application/breadcrumbs"
|
||||
= page_stylesheet_link_tag "alt_styles/edit"
|
|
@ -1,4 +1,5 @@
|
|||
- title "Styling Studio"
|
||||
- use_responsive_design
|
||||
|
||||
%p
|
||||
Here's all the new NC Pet Styles we have! They're available in the app too,
|
||||
|
@ -13,6 +14,24 @@
|
|||
wearable items, there's not a great way for us to get style tokens onto
|
||||
tradelists… this may change someday, but probably not soon, sorry!
|
||||
|
||||
- @alt_styles.group_by(&:species).each do |species, species_styles|
|
||||
%h2.alt-styles-header= species.human_name
|
||||
%ul.alt-styles-list= render partial: "alt_style", collection: species_styles
|
||||
= form_with url: alt_styles_path, method: :get,
|
||||
class: "rainbow-pool-filters" do |f|
|
||||
%fieldset
|
||||
%legend Filter by:
|
||||
= f.select :series, @all_series_names,
|
||||
selected: @series_name, include_blank: "Style…"
|
||||
= f.select :color, @all_color_names,
|
||||
selected: @color&.human_name, include_blank: "Color…"
|
||||
= f.select :species, @all_species_names,
|
||||
selected: @species&.human_name, include_blank: "Species…"
|
||||
= f.submit "Go"
|
||||
|
||||
= will_paginate @alt_styles, class: "rainbow-pool-pagination"
|
||||
|
||||
%ul.rainbow-pool-list= render @alt_styles
|
||||
|
||||
= will_paginate @alt_styles, class: "rainbow-pool-pagination"
|
||||
|
||||
- content_for :stylesheets do
|
||||
= stylesheet_link_tag "application/rainbow-pool"
|
||||
= page_stylesheet_link_tag "alt_styles/index"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
%outfit-viewer
|
||||
- html_options = {} unless defined? html_options
|
||||
= content_tag "outfit-viewer", **html_options do
|
||||
.loading-indicator= render partial: "hanger_spinner"
|
||||
|
||||
%label.play-pause-button{title: "Pause/play animations"}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
%li
|
||||
= link_to useful_pet_state_path(pet_state.pet_type, pet_state) do
|
||||
= outfit_viewer pet_state:
|
||||
= outfit_viewer pet_state:, class: "preview"
|
||||
.name= pose_name pet_state.pose
|
||||
- if pet_state.glitched?
|
||||
%span.glitched{title: "Glitched"} 👾
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
%li
|
||||
= link_to pet_type do
|
||||
= pet_type_image pet_type, :happy, :thumb
|
||||
= pet_type_image pet_type, :happy, :thumb, class: "preview"
|
||||
.name= pet_type.human_name
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
- title "Rainbow Pool"
|
||||
- use_responsive_design
|
||||
|
||||
= form_with method: :get, class: "pet-filters" do |form|
|
||||
= form_with method: :get, class: "rainbow-pool-filters" do |form|
|
||||
%fieldset
|
||||
%legend Filter by:
|
||||
= form.select :color, @color_names, selected: @selected_color&.human_name, include_blank: "Color…"
|
||||
= form.select :species, @species_names, selected: @selected_species&.human_name, include_blank: "Species…"
|
||||
= form.submit "Go"
|
||||
|
||||
= will_paginate @pet_types
|
||||
= will_paginate @pet_types, class: "rainbow-pool-pagination"
|
||||
|
||||
%ui.pet-types= render @pet_types
|
||||
%ui.rainbow-pool-list= render @pet_types
|
||||
|
||||
= will_paginate @pet_types
|
||||
= will_paginate @pet_types, class: "rainbow-pool-pagination"
|
||||
|
||||
- content_for :stylesheets do
|
||||
= page_stylesheet_link_tag "pet_types/index"
|
||||
= stylesheet_link_tag "application/rainbow-pool"
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
%li
|
||||
Appearances
|
||||
|
||||
%ul.pet-states
|
||||
%ul.rainbow-pool-list
|
||||
= render @pet_states[:canonical]
|
||||
|
||||
- if @pet_states[:other].present?
|
||||
|
@ -26,6 +26,7 @@
|
|||
= stylesheet_link_tag "application/breadcrumbs"
|
||||
= stylesheet_link_tag "application/hanger-spinner"
|
||||
= stylesheet_link_tag "application/outfit-viewer"
|
||||
= stylesheet_link_tag "application/rainbow-pool"
|
||||
= page_stylesheet_link_tag "pet_types/show"
|
||||
|
||||
- content_for :javascripts do
|
||||
|
|
|
@ -35,7 +35,7 @@ OpenneoImpressItems::Application.routes.draw do
|
|||
end
|
||||
resources :alt_styles, path: 'alt-styles', only: [:index]
|
||||
end
|
||||
resources :alt_styles, path: 'alt-styles', only: [:index]
|
||||
resources :alt_styles, path: 'alt-styles', only: [:index, :edit, :update]
|
||||
resources :swf_assets, path: 'swf-assets', only: [:show]
|
||||
resources :pet_types, path: 'rainbow-pool', param: "name",
|
||||
only: [:index, :show] do
|
||||
|
|
Loading…
Reference in a new issue