Compare commits

..

17 commits

Author SHA1 Message Date
e4a640ccee Oops, fix minor breadcrumbs display bug on pet_types#show 2024-09-27 22:39:22 -07:00
d465f4125e For support staff, Rainbow Pool links directly to edit page, not show
Just on the assumption that like. We're mostly here to edit things
2024-09-27 22:35:13 -07:00
946a6326ac Use radio buttons for poses in Rainbow Pool form, instead of dropdown
Just a bit easier to find what you want! especially with the grid layout
2024-09-27 22:34:52 -07:00
d5a901b917 Add edit form to Rainbow Pool for pet states, for support staff only 2024-09-27 22:14:00 -07:00
39e5ca59c4 Add breadcrumbs to Rainbow Pool pages 2024-09-27 20:01:07 -07:00
4fa80d33cc Merge branch 'main' into rainbow-pool 2024-09-27 19:43:31 -07:00
f8a5ce4490 Improve Rainbow Pool filter form styles 2024-09-27 19:10:37 -07:00
81f0845d4a Improve Rainbow Pool link styles 2024-09-27 18:45:45 -07:00
f0257ba2d3 Merge branch 'main' into rainbow-pool 2024-09-27 18:32:04 -07:00
80307f21f7 Add Rainbow Pool homepage, with basic filter form 2024-09-26 21:10:25 -07:00
75040ffbf3 Add pages for the Rainbow Pool pet states 2024-09-26 20:24:31 -07:00
6f45cd0485 Add a bit more info to Rainbow Pool glitched label 2024-09-26 19:34:30 -07:00
4e33477c65 Hide unconverted below the "Other" list for Rainbow Pool poses 2024-09-26 19:33:16 -07:00
b28255cafd WIP: Better styles for Rainbow Pool pet type page 2024-09-26 18:39:32 -07:00
99e8b46157 Oops, fix bug parsing "8-Bit-Chia" in Rainbow Pool URLs 2024-09-26 18:36:49 -07:00
734b7fba1d WIP: Outfit viewers on pet type Rainbow Pool page
Now that we have such a convenient lil outfit viewer component we built
for the item page preview, it's easy peasy to drop it in here too! And
it's all nice and lightweight, since in this case it's basically just.
image tags, with some supporting enhancements.

Anyway, this page has no actual useful styles of its own yet. Gonna
make it look nice and such!
2024-09-26 18:20:05 -07:00
a1d6961249 WIP: Placeholder page for Rainbow Pool pet type
I'm experimenting with a Rainbow Pool ish UI, mainly as a support tool
for exploring and labeling poses—but one we can probably just show to
real users too!

Right now, I just use pet type images as a placeholder, and I polished
up some of the `pet_type_image` API. But we're probably gonna drop
these for a full outfit viewer, now that I think of it.
2024-09-26 14:56:45 -07:00
24 changed files with 663 additions and 112 deletions

View file

@ -0,0 +1,23 @@
#title:has(+ .breadcrumbs)
margin-bottom: .125em
.breadcrumbs
list-style-type: none
display: flex
flex-direction: row
margin-block: .5em
font-size: .85em
li
display: flex
li:not(:first-child)
&::before
margin-inline: .35em
content: ""
&[data-relation-to-prev=sibling]::before
content: "+"
&[data-relation-to-prev=menu]::before
content: "-"

View file

@ -0,0 +1,110 @@
@import "../partials/clean/constants"
// When loading, fade in the loading spinner after a brief delay. We only apply
// the delay here, not on the base styles, because fading *out* on load should
// be instant.
//
// This is implemented as a mixin, so that the item page can leverage the same
// loading state when loading a new preview altogether. Once CSS container
// style queries gain wider support, maybe use that instead.
=outfit-viewer-loading
cursor: wait
.loading-indicator
opacity: 1
transition-delay: 2s
// If the outfit *starts* in loading state, still delay the fade-in.
@starting-style
opacity: 0
outfit-viewer
display: block
position: relative
overflow: hidden
// These are default widths, expected to often be overridden.
width: 300px
height: 300px
// There's no useful text in here, but double-clicking the play/pause
// button can cause a weird selection state. Disable text selection.
user-select: none
-webkit-user-select: none
outfit-layer
display: block
position: absolute
inset: 0
// We disable pointer-events most importantly for the iframes, which
// will ignore our `cursor: wait` and show a plain cursor for the
// inside of its own document. But also, the context menus for these
// elements are kinda actively misleading, too!
pointer-events: none
img, iframe
width: 100%
height: 100%
.loading-indicator
position: absolute
z-index: 1000
bottom: 0px
right: 4px
padding: 8px
background: radial-gradient(circle closest-side, white 45%, #ffffff00)
opacity: 0
.play-pause-button
position: absolute
z-index: 1001
left: 8px
bottom: 8px
display: none
align-items: center
justify-content: center
color: white
background: rgba(0, 0, 0, 0.64)
width: 2.5em
height: 2.5em
border-radius: 100%
border: 2px solid transparent
transition: all .25s
.playing-label, .paused-label
display: none
width: 1em
height: 1em
.play-pause-toggle
// Visually hidden
clip: rect(0 0 0 0)
clip-path: inset(50%)
height: 1px
overflow: hidden
position: absolute
white-space: nowrap
width: 1px
&:checked ~ .playing-label
display: block
&:not(:checked) ~ .paused-label
display: block
&:hover, &:has(.play-pause-toggle:focus)
border: 2px solid $module-border-color
background: $module-bg-color
color: $text-color
&:has(.play-pause-toggle:active)
transform: translateY(2px)
&:has(outfit-layer:state(has-animations))
.play-pause-button
display: flex
&:has(outfit-layer:state(loading))
+outfit-viewer-loading

View file

@ -2,6 +2,8 @@
@import "../partials/clean/mixins" @import "../partials/clean/mixins"
@import "../partials/item_header" @import "../partials/item_header"
@import "../application/outfit-viewer"
#container #container
width: 900px // A bit more generous to the preview area! width: 900px // A bit more generous to the preview area!
@ -78,93 +80,10 @@
width: var(--natural-width) width: var(--natural-width)
outfit-viewer outfit-viewer
display: block
position: relative
width: 300px width: 300px
height: 300px height: 300px
border: 1px solid $module-border-color border: 1px solid $module-border-color
border-radius: 1em border-radius: 1em
overflow: hidden
// There's no useful text in here, but double-clicking the play/pause
// button can cause a weird selection state. Disable text selection.
user-select: none
-webkit-user-select: none
outfit-layer
display: block
position: absolute
inset: 0
// We disable pointer-events most importantly for the iframes, which
// will ignore our `cursor: wait` and show a plain cursor for the
// inside of its own document. But also, the context menus for these
// elements are kinda actively misleading, too!
pointer-events: none
img, iframe
width: 100%
height: 100%
.loading-indicator
position: absolute
z-index: 1000
bottom: 0px
right: 4px
padding: 8px
background: radial-gradient(circle closest-side, white 45%, #ffffff00)
opacity: 0
transition: opacity .5s
.play-pause-button
position: absolute
z-index: 1001
left: 8px
bottom: 8px
display: none
align-items: center
justify-content: center
color: white
background: rgba(0, 0, 0, 0.64)
width: 2.5em
height: 2.5em
border-radius: 100%
border: 2px solid transparent
transition: all .25s
.playing-label, .paused-label
display: none
width: 1em
height: 1em
.play-pause-toggle
// Visually hidden
clip: rect(0 0 0 0)
clip-path: inset(50%)
height: 1px
overflow: hidden
position: absolute
white-space: nowrap
width: 1px
&:checked ~ .playing-label
display: block
&:not(:checked) ~ .paused-label
display: block
&:hover, &:has(.play-pause-toggle:focus)
border: 2px solid $module-border-color
background: $module-bg-color
color: $text-color
&:has(.play-pause-toggle:active)
transform: translateY(2px)
&:has(outfit-layer:state(has-animations))
.play-pause-button
display: flex
.error-indicator .error-indicator
font-size: 85% font-size: 85%
@ -179,16 +98,8 @@ outfit-viewer
// //
// We only apply the delay here, not on the base styles, because fading // We only apply the delay here, not on the base styles, because fading
// *out* on load should be instant. // *out* on load should be instant.
#item-preview[busy] outfit-viewer, outfit-viewer:has(outfit-layer:state(loading)) #item-preview[busy] outfit-viewer
cursor: wait +outfit-viewer-loading
.loading-indicator
opacity: 1
transition-delay: 2s
// If the outfit *starts* in loading state, still delay the fade-in.
@starting-style
opacity: 0
#item-preview:has(outfit-layer:state(error)) #item-preview:has(outfit-layer:state(error))
outfit-viewer outfit-viewer

View file

@ -0,0 +1,25 @@
@import "../partials/clean/constants"
outfit-viewer
margin: 0 auto
.pose-options
list-style-type: none
display: grid
grid-template-columns: 1fr 1fr 1fr
gap: .25em
label
display: flex
align-items: center
gap: .5em
padding: .5em 1em
border: 1px solid $soft-border-color
border-radius: 1em
input
margin: 0
&:has(:checked)
background: $module-bg-color
border-color: $module-border-color

View file

@ -0,0 +1,5 @@
outfit-viewer
margin: 0 auto
dt
cursor: help

View file

@ -0,0 +1,55 @@
@import "../partials/clean/constants"
.pet-filters
fieldset
display: flex
flex-direction: row
align-items: center
justify-content: center
gap: .5em
legend
display: contents
font-weight: bold
[role=navigation]
margin-block: .5em
text-align: center
.pet-types
list-style-type: none
display: flex
flex-wrap: wrap
justify-content: center
gap: .5em
> li
width: 150px
max-width: calc(50% - .25em)
min-width: 150px
box-sizing: border-box
text-align: center
a
display: block
border-radius: 1em
padding: .5em
text-decoration: none
background: white
&:hover
outline: 1px solid $module-border-color
background: $module-bg-color
img
width: 100%
height: auto
aspect-ratio: 1 / 1
margin-bottom: -1em
.name
background: inherit
padding: .25em .5em
border-radius: .5em
margin: 0 auto
position: relative
z-index: 1

View file

@ -0,0 +1,43 @@
@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
.glitched
cursor: help

View file

@ -110,5 +110,11 @@ class ApplicationController < ActionController::Base
Rails.logger.debug "Using return_to path: #{return_to.inspect}" Rails.logger.debug "Using return_to path: #{return_to.inspect}"
return_to || root_path return_to || root_path
end end
def support_staff_only
unless current_user&.support_staff?
raise AccessDenied, "Support staff only"
end
end
end end

View file

@ -0,0 +1,30 @@
class PetStatesController < ApplicationController
before_action :find_pet_state
before_action :support_staff_only, except: [:show]
def show
end
def edit
end
def update
if @pet_state.update(pet_state_params)
flash[:notice] = "Pet appearance \##{@pet_state.id} successfully saved!"
redirect_to @pet_type
else
render action: :edit, status: :bad_request
end
end
protected
def find_pet_state
@pet_type = PetType.matching_name_param(params[:pet_type_name]).first!
@pet_state = @pet_type.pet_states.find(params[:id])
end
def pet_state_params
params.require(:pet_state).permit(:pose, :glitched)
end
end

View file

@ -1,10 +1,78 @@
class PetTypesController < ApplicationController class PetTypesController < ApplicationController
def show def index
@pet_type = PetType. @species_names = Species.order(:name).map(&:human_name)
where(species_id: params[:species_id]). @color_names = Color.order(:name).map(&:human_name)
where(color_id: params[:color_id]).
first
render json: @pet_type if params[:species].present?
@selected_species = Species.find_by!(name: params[:species])
@selected_species_name = @selected_species.human_name
end
if params[:color].present?
@selected_color = Color.find_by!(name: params[:color])
@selected_color_name = @selected_color.human_name
end
@pet_types = PetType.
includes(:color, :species).
order(created_at: :desc).
paginate(page: params[:page], per_page: 30)
if @selected_species
@pet_types = @pet_types.where(species_id: @selected_species)
end
if @selected_color
@pet_types = @pet_types.where(color_id: @selected_color)
end
end
def show
@pet_type = find_pet_type
respond_to do |format|
format.html do
@pet_states = group_pet_states @pet_type.pet_states
end
format.json { render json: @pet_type }
end
end
protected
# The API-ish route uses IDs, but the human-facing route uses names.
def find_pet_type
if params[:species_id] && params[:color_id]
PetType.find_by!(
species_id: params[:species_id],
color_id: params[:color_id],
)
elsif params[:name]
color_name, _, species_name = params[:name].rpartition("-")
raise ActiveRecord::RecordNotFound if species_name.blank?
PetType.matching_name(color_name, species_name).first!
else
raise "expected params: species_id and color_id, or name"
end
end
# The `canonical` pet states are the main ones we want to show: the most
# canonical state for each pose. The `other` pet states are, the others!
#
# If no main poses are available, then we just make all the poses
# "canonical", and show the whole mish-mash!
MAIN_POSES = %w(HAPPY_FEM HAPPY_MASC SAD_FEM SAD_MASC SICK_FEM SICK_MASC)
def group_pet_states(pet_states)
pose_groups = pet_states.emotion_order.group_by(&:pose)
main_groups = pose_groups.select { |k| MAIN_POSES.include?(k) }.values
other_groups = pose_groups.reject { |k| MAIN_POSES.include?(k) }.values
if main_groups.empty?
return {canonical: other_groups.flatten(1).sort_by(&:pose), other: []}
end
canonical = main_groups.map(&:first).sort_by(&:pose)
main_others = main_groups.map { |l| l.drop(1) }.flatten(1)
other = (main_others + other_groups.flatten(1)).sort_by(&:pose)
{canonical:, other:}
end end
end end

View file

@ -14,19 +14,30 @@ module ItemsHelper
} }
Sizes = { Sizes = {
face: 1, face: 1, # 50x50
thumb: 2, face_3x: 6, # 150x150
zoom: 3,
full: 4, thumb: 2, # 150x150
face_2x: 6, full: 4, # 300x300
large: 5, # 500x500
xlarge: 7, # 640x640
zoom: 3, # 80x80
autocrop: 9, # <varies>
}
SizeUpgrades = {
face: :face_3x,
thumb: :full,
full: :xlarge,
} }
end end
def pet_type_image_url(pet_type, emotion: :happy, size: :face) def pet_type_image_url(pet_type, emotion: :happy, size: :face)
PetTypeImage::Template.expand( PetTypeImage::Template.expand(
hash: pet_type.basic_image_hash || pet_type.image_hash, hash: pet_type.basic_image_hash || pet_type.image_hash,
emotion: PetTypeImage::Emotions[emotion], emotion: PetTypeImage::Emotions.fetch(emotion),
size: PetTypeImage::Sizes[size], size: PetTypeImage::Sizes.fetch(size),
).to_s ).to_s
end end
@ -246,8 +257,10 @@ module ItemsHelper
def pet_type_image(pet_type, emotion, size, **options) def pet_type_image(pet_type, emotion, size, **options)
src = pet_type_image_url(pet_type, emotion:, size:) src = pet_type_image_url(pet_type, emotion:, size:)
srcset = if size == :face
[[pet_type_image_url(pet_type, emotion:, size: :face_2x), "2x"]] size_2x = PetTypeImage::SizeUpgrades[size]
srcset = if size_2x
[[pet_type_image_url(pet_type, emotion:, size: size_2x), "2x"]]
end end
image_tag(src, srcset:, **options) image_tag(src, srcset:, **options)

View file

@ -69,5 +69,17 @@ module OutfitsHelper
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
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
render partial: "outfit_viewer", locals: {outfit:}
end
end end

View file

@ -0,0 +1,36 @@
module PetStatesHelper
def pose_name(pose)
case pose
when "HAPPY_FEM"
"Happy (Feminine)"
when "HAPPY_MASC"
"Happy (Masculine)"
when "SAD_FEM"
"Sad (Feminine)"
when "SAD_MASC"
"Sad (Masculine)"
when "SICK_FEM"
"Sick (Feminine)"
when "SICK_MASC"
"Sick (Masculine)"
when "UNCONVERTED"
"Unconverted"
else
"Not labeled yet"
end
end
POSE_OPTIONS = %w(HAPPY_FEM SAD_FEM SICK_FEM HAPPY_MASC SAD_MASC SICK_MASC
UNCONVERTED UNKNOWN)
def pose_options
POSE_OPTIONS
end
def useful_pet_state_path(...)
if support_staff?
edit_pet_type_pet_state_path(...)
else
pet_type_pet_state_path(...)
end
end
end

View file

@ -71,6 +71,28 @@ class PetState < ApplicationRecord
end end
end end
# TODO: More and more, wanting to refactor poses…
def pose=(pose)
case pose
when "UNKNOWN"
label_pose nil, nil, unconverted: nil, labeled: false
when "HAPPY_MASC"
label_pose 1, false
when "HAPPY_FEM"
label_pose 1, true
when "SAD_MASC"
label_pose 2, false
when "SAD_FEM"
label_pose 2, true
when "SICK_MASC"
label_pose 4, false
when "SICK_FEM"
label_pose 4, true
when "UNCONVERTED"
label_pose nil, nil, unconverted: true
end
end
def reassign_children_to!(main_pet_state) def reassign_children_to!(main_pet_state)
self.contributions.each do |contribution| self.contributions.each do |contribution|
contribution.contributed = main_pet_state contribution.contributed = main_pet_state
@ -118,6 +140,10 @@ class PetState < ApplicationRecord
end end
end end
def to_param
"#{id}-#{pose.split('_').map(&:capitalize).join('-')}"
end
def self.from_pet_type_and_biology_info(pet_type, info) def self.from_pet_type_and_biology_info(pet_type, info)
swf_asset_ids = [] swf_asset_ids = []
info.each do |zone_id, asset_info| info.each do |zone_id, asset_info|
@ -171,5 +197,15 @@ class PetState < ApplicationRecord
pet_state.parent_swf_asset_relationships_to_update = relationships pet_state.parent_swf_asset_relationships_to_update = relationships
pet_state pet_state
end end
private
# A helper for the `pose=` method.
def label_pose(mood_id, female, unconverted: false, labeled: true)
self.labeled = labeled
self.mood_id = mood_id
self.female = female
self.unconverted = unconverted
end
end end

View file

@ -15,6 +15,10 @@ class PetType < ApplicationRecord
species = Species.find_by_name!(species_name) species = Species.find_by_name!(species_name)
where(color_id: color.id, species_id: species.id) where(color_id: color.id, species_id: species.id)
} }
scope :matching_name_param, ->(name_param) {
color_name, _, species_name = name_param.rpartition("-")
matching_name(color_name, species_name)
}
scope :preferring_species, ->(species_id) { scope :preferring_species, ->(species_id) {
joins(:species).order([Arel.sql("species_id = ? DESC"), species_id]) joins(:species).order([Arel.sql("species_id = ? DESC"), species_id])
} }
@ -108,6 +112,10 @@ class PetType < ApplicationRecord
Item.appearances_for(item, self, ...) Item.appearances_for(item, self, ...)
end end
def to_param
"#{color.human_name}-#{species.human_name}"
end
def self.all_by_ids_or_children(ids, pet_states) def self.all_by_ids_or_children(ids, pet_states)
pet_states_by_pet_type_id = {} pet_states_by_pet_type_id = {}
pet_states.each do |pet_state| pet_states.each do |pet_state|

View file

@ -21,6 +21,6 @@
- if swf_asset.canvas_movie? - if swf_asset.canvas_movie?
%iframe{src: swf_asset_path(swf_asset, playing: outfit_viewer_is_playing ? true : nil)} %iframe{src: swf_asset_path(swf_asset, playing: outfit_viewer_is_playing ? true : nil)}
- elsif swf_asset.image_url.present? - elsif swf_asset.image_url.present?
= image_tag swf_asset.image_url, alt: "" = image_tag swf_asset.image_url, alt: "", loading: "lazy"
- else - else
/ No movie or image available for SWF asset: #{swf_asset.url} / No movie or image available for SWF asset: #{swf_asset.url}

View file

@ -16,7 +16,7 @@
= turbo_frame_tag "item-preview" do = turbo_frame_tag "item-preview" do
.preview-area .preview-area
= render partial: "outfit_viewer", locals: {outfit: @preview_outfit} = outfit_viewer @preview_outfit
.error-indicator .error-indicator
💥 We couldn't load all of this outfit. Try again? 💥 We couldn't load all of this outfit. Try again?
= link_to wardrobe_path(params: @preview_outfit.wardrobe_params), = link_to wardrobe_path(params: @preview_outfit.wardrobe_params),
@ -122,6 +122,8 @@
- content_for :stylesheets do - content_for :stylesheets do
= stylesheet_link_tag "application/hanger-spinner" = stylesheet_link_tag "application/hanger-spinner"
-# This is imported into items/show directly, to gain access to its mixins.
-# = stylesheet_link_tag "application/outfit-viewer"
= page_stylesheet_link_tag "layouts/items" = page_stylesheet_link_tag "layouts/items"
= page_stylesheet_link_tag "items/show" = page_stylesheet_link_tag "items/show"

View file

@ -0,0 +1,6 @@
%li
= link_to useful_pet_state_path(pet_state.pet_type, pet_state) do
= outfit_viewer pet_state:
.name= pose_name pet_state.pose
- if pet_state.glitched?
%span.glitched{title: "Glitched"} 👾

View file

@ -0,0 +1,50 @@
- title "#{@pet_type.human_name}: #{pose_name @pet_state.pose}"
- use_responsive_design
%ol.breadcrumbs
%li
= link_to "Rainbow Pool", pet_types_path
%li
= link_to @pet_type.color.human_name,
pet_types_path(color: @pet_type.color.human_name)
%li{"data-relation-to-prev": "sibling"}
= link_to @pet_type.species.human_name,
pet_types_path(species: @pet_type.species.human_name)
%li
= link_to "Appearances", @pet_type
%li
= link_to "\##{@pet_state.id}", [@pet_type, @pet_state]
%li
Edit
= outfit_viewer pet_state: @pet_state
= form_with model: [@pet_type, @pet_state] do |f|
- if @pet_state.errors.any?
%p
Could not save:
%ul.errors
- @pet_state.errors.each do |error|
%li= error.full_message
%dl
%dt Pose
%dd
%ul.pose-options
- pose_options.each do |pose|
%li
%label
= f.radio_button :pose, pose
= pose_name pose
%dt Glitched?
%dd
= f.select :glitched, [["✅ Not marked as Glitched", false],
["👾 Yes, it's bad news bonko'd", true]]
= f.submit "Save"
- content_for :stylesheets do
= stylesheet_link_tag "application/breadcrumbs"
= stylesheet_link_tag "application/outfit-viewer"
= page_stylesheet_link_tag "pet_states/edit"
- content_for :javascripts do
= javascript_include_tag "outfit-viewer"

View file

@ -0,0 +1,54 @@
- title "#{@pet_type.human_name}: #{pose_name @pet_state.pose}"
- use_responsive_design
%ol.breadcrumbs
%li
= link_to "Rainbow Pool", pet_types_path
%li
= link_to @pet_type.color.human_name,
pet_types_path(color: @pet_type.color.human_name)
%li{"data-relation-to-prev": "sibling"}
= link_to @pet_type.species.human_name,
pet_types_path(species: @pet_type.species.human_name)
%li
= link_to "Appearances", @pet_type
%li
\##{@pet_state.id}
- if support_staff?
%li{"data-relation-to-prev": "menu"}
= link_to "Edit", edit_pet_type_pet_state_path(@pet_type, @pet_state)
= outfit_viewer pet_state: @pet_state
%dl
%dt{title: "Pose usually affects just the eyes and mouth. Neopets " +
"genders these as Male/Female, but I don't like those " +
"terms for like… it's just eyelashes! Sheesh!"}
Pose
%dd
= pose_name @pet_state.pose
- if @pet_state.pose == "UNCONVERTED"
(Retired, replaced by #{link_to "Alt Styles", alt_styles_path})
%dt{title: "This is our own internal ID number, nothing to do with " +
"Neopets's official data."}
DTI ID
%dd= @pet_state.id
%dt{title: "When we notice a form looks wrong, we mark it Glitched, to " +
"tell our systems to prefer other forms for this pose instead."}
Glitched?
%dd
- if @pet_state.glitched?
👾 Yes, it's bad news bonko'd
- else
✅ Not marked as Glitched
- content_for :stylesheets do
= stylesheet_link_tag "application/breadcrumbs"
= stylesheet_link_tag "application/hanger-spinner"
= stylesheet_link_tag "application/outfit-viewer"
= page_stylesheet_link_tag "pet_states/show"
- content_for :javascripts do
= javascript_include_tag "outfit-viewer", async: true

View file

@ -0,0 +1,4 @@
%li
= link_to pet_type do
= pet_type_image pet_type, :happy, :thumb
.name= pet_type.human_name

View file

@ -0,0 +1,18 @@
- title "Rainbow Pool"
- use_responsive_design
= form_with method: :get, class: "pet-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
%ui.pet-types= render @pet_types
= will_paginate @pet_types
- content_for :stylesheets do
= page_stylesheet_link_tag "pet_types/index"

View file

@ -0,0 +1,32 @@
- title "#{@pet_type.human_name}"
- use_responsive_design
%ol.breadcrumbs
%li
= link_to "Rainbow Pool", pet_types_path
%li
= link_to @pet_type.color.human_name,
pet_types_path(color: @pet_type.color.human_name)
%li{"data-relation-to-prev": "sibling"}
= link_to @pet_type.species.human_name,
pet_types_path(species: @pet_type.species.human_name)
%li
Appearances
%ul.pet-states
= render @pet_states[:canonical]
- if @pet_states[:other].present?
%details
%summary Other
%ul.pet-states
= render @pet_states[:other]
- content_for :stylesheets do
= stylesheet_link_tag "application/breadcrumbs"
= stylesheet_link_tag "application/hanger-spinner"
= stylesheet_link_tag "application/outfit-viewer"
= page_stylesheet_link_tag "pet_types/show"
- content_for :javascripts do
= javascript_include_tag "outfit-viewer", async: true

View file

@ -37,6 +37,10 @@ OpenneoImpressItems::Application.routes.draw do
end end
resources :alt_styles, path: 'alt-styles', only: [:index] resources :alt_styles, path: 'alt-styles', only: [:index]
resources :swf_assets, path: 'swf-assets', only: [:show] resources :swf_assets, path: 'swf-assets', only: [:show]
resources :pet_types, path: 'rainbow-pool', param: "name",
only: [:index, :show] do
resources :pet_states, only: [:show, :edit, :update], path: "appearances"
end
# Loading and modeling pets! # Loading and modeling pets!
post '/pets/load' => 'pets#load', :as => :load_pet post '/pets/load' => 'pets#load', :as => :load_pet