Compare commits
15 commits
5e6c2c3c3f
...
9838301712
Author | SHA1 | Date | |
---|---|---|---|
9838301712 | |||
cf1d28dd62 | |||
d9902c6afa | |||
a70b70be7d | |||
d5752eac2a | |||
302c116c8f | |||
568c30fa90 | |||
b137eed4c4 | |||
965725f9e9 | |||
341a8dd89c | |||
3d6abc84dd | |||
cec29682c4 | |||
589d728c76 | |||
2e3d5d2020 | |||
97abd6e438 |
9 changed files with 234 additions and 110 deletions
|
@ -37,3 +37,34 @@ body.items-show
|
||||||
.nc-icon
|
.nc-icon
|
||||||
height: 16px
|
height: 16px
|
||||||
width: 16px
|
width: 16px
|
||||||
|
|
||||||
|
outfit-viewer
|
||||||
|
position: relative
|
||||||
|
display: block
|
||||||
|
width: 300px
|
||||||
|
height: 300px
|
||||||
|
border: 1px solid $module-border-color
|
||||||
|
border-radius: 1em
|
||||||
|
overflow: hidden
|
||||||
|
|
||||||
|
margin: 0 auto .75em
|
||||||
|
|
||||||
|
outfit-layer
|
||||||
|
display: block
|
||||||
|
position: absolute
|
||||||
|
inset: 0
|
||||||
|
|
||||||
|
img
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.species-color-picker
|
||||||
|
.error-icon
|
||||||
|
cursor: help
|
||||||
|
margin-right: .25em
|
||||||
|
|
||||||
|
&[data-is-valid="false"]
|
||||||
|
select
|
||||||
|
border-color: $error-border-color
|
||||||
|
color: $error-color
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
/* When item names get long, don't let the buttons wrap to give the
|
/* When item names get long, don't let the buttons wrap to give the
|
||||||
* item names more space. The names should wrap more instead! */
|
* item names more space. The names should wrap more instead! */
|
||||||
text-wrap: nowrap
|
text-wrap: nowrap
|
||||||
|
margin: .25em
|
||||||
|
|
||||||
tbody
|
tbody
|
||||||
tr
|
tr
|
||||||
|
@ -55,14 +56,12 @@
|
||||||
th
|
th
|
||||||
text-align: left
|
text-align: left
|
||||||
|
|
||||||
|
.name-cell
|
||||||
|
text-wrap: nowrap
|
||||||
|
|
||||||
.thumbnail-cell img
|
.thumbnail-cell img
|
||||||
outline: 1px solid $soft-border-color
|
outline: 1px solid $soft-border-color
|
||||||
|
|
||||||
.actions-cell
|
|
||||||
button, a.button
|
|
||||||
/* Bootstrap's Purple 600 */
|
|
||||||
+awesome-button-color(#59359a)
|
|
||||||
|
|
||||||
tr[data-item-owned]
|
tr[data-item-owned]
|
||||||
color: #aaa
|
color: #aaa
|
||||||
|
|
||||||
|
@ -115,6 +114,15 @@
|
||||||
text-decoration-line: underline
|
text-decoration-line: underline
|
||||||
text-decoration-style: dotted
|
text-decoration-style: dotted
|
||||||
|
|
||||||
|
.actions-cell
|
||||||
|
button, a.button
|
||||||
|
&[data-action-kind=bulk-nc-mall]
|
||||||
|
/* Bootstrap's Purple 600 */
|
||||||
|
+awesome-button-color(#59359a)
|
||||||
|
|
||||||
|
&[data-complexity="high"]
|
||||||
|
width: 70%
|
||||||
|
|
||||||
/* For wearable items that belong to a specific set that all come together,
|
/* For wearable items that belong to a specific set that all come together,
|
||||||
* like a Paint Brush. */
|
* like a Paint Brush. */
|
||||||
&[data-group-type="bundle"]
|
&[data-group-type="bundle"]
|
||||||
|
|
|
@ -82,6 +82,17 @@ class ItemsController < ApplicationController
|
||||||
group_by_owned
|
group_by_owned
|
||||||
@current_user_quantities = current_user.item_quantities_for(@item)
|
@current_user_quantities = current_user.item_quantities_for(@item)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@selected_preview_pet_type = load_selected_preview_pet_type
|
||||||
|
@preview_pet_type = load_preview_pet_type
|
||||||
|
|
||||||
|
@item_layers = @item.appearance_for(
|
||||||
|
@preview_pet_type, swf_asset_includes: [:zone]
|
||||||
|
).swf_assets
|
||||||
|
@pet_layers = @preview_pet_type.canonical_pet_state.swf_assets.
|
||||||
|
includes(:zone)
|
||||||
|
|
||||||
|
@preview_error = validate_preview
|
||||||
end
|
end
|
||||||
|
|
||||||
format.gif do
|
format.gif do
|
||||||
|
@ -189,6 +200,38 @@ class ItemsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def load_selected_preview_pet_type
|
||||||
|
color_id = params.dig(:preview, :color_id)
|
||||||
|
species_id = params.dig(:preview, :species_id)
|
||||||
|
|
||||||
|
return load_default_preview_pet_type if color_id.nil? || species_id.nil?
|
||||||
|
|
||||||
|
PetType.find_or_initialize_by(color_id:, species_id:)
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_preview_pet_type
|
||||||
|
if @selected_preview_pet_type.persisted?
|
||||||
|
@selected_preview_pet_type
|
||||||
|
else
|
||||||
|
load_default_preview_pet_type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_default_preview_pet_type
|
||||||
|
PetType.find_by_color_id_and_species_id(
|
||||||
|
Color.find_by_name("Blue"),
|
||||||
|
Species.find_by_name("Acara"),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_preview
|
||||||
|
if @selected_preview_pet_type.new_record?
|
||||||
|
:pet_type_does_not_exist
|
||||||
|
elsif @item_layers.empty?
|
||||||
|
:no_item_data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def search_error(e)
|
def search_error(e)
|
||||||
@items = []
|
@items = []
|
||||||
@query = params[:q]
|
@query = params[:q]
|
||||||
|
|
|
@ -194,50 +194,34 @@ module ItemsHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def dyeworks_nc_total_for(items)
|
def dyeworks_nc_total_for(items)
|
||||||
dyeworks_items_nc_total_for(items) + dyeworks_potions_nc_total(items.size)
|
|
||||||
end
|
|
||||||
|
|
||||||
def dyeworks_items_nc_total_for(items)
|
|
||||||
nc_total_for items.map(&:dyeworks_base_item)
|
nc_total_for items.map(&:dyeworks_base_item)
|
||||||
end
|
end
|
||||||
|
|
||||||
def dyeworks_potions_nc_total(num_items)
|
def dyeworks_average_num_potions_for(items)
|
||||||
dyeworks_potions_nc_breakdown(num_items)[:nc_total]
|
# Compute the number of expected potions for each (inverse of the odds),
|
||||||
|
# sum them, then round up.
|
||||||
|
items.map { |i| 1 / i.dyeworks_odds }.sum.ceil
|
||||||
end
|
end
|
||||||
|
|
||||||
def dyeworks_potions_nc_summary(num_items)
|
def dyeworks_estimated_potions_cost_for(items)
|
||||||
dyeworks_potions_nc_breakdown(num_items)[:summary]
|
# NOTE: You could do bundles too, but let's just keep it simple.
|
||||||
|
dyeworks_average_num_potions_for(items) * 125
|
||||||
end
|
end
|
||||||
|
|
||||||
def dyeworks_potions_nc_breakdown(num_items)
|
def complexity_for(items)
|
||||||
nc_total = 0
|
max_name_length = items.map(&:name).map(&:length).max
|
||||||
summaries = []
|
max_name_length >= 40 ? "high" : "low"
|
||||||
|
end
|
||||||
|
|
||||||
# For every 10 potions, buy a 10-Bundle for 900 NC.
|
def probability(p)
|
||||||
while num_items >= 10
|
case p
|
||||||
nc_total += 900
|
when 1
|
||||||
summaries << "10-Bundle (900 NC)"
|
"100%"
|
||||||
num_items -= 10
|
when 0
|
||||||
|
"0%"
|
||||||
|
else
|
||||||
|
"#{p.numerator} in #{p.denominator}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# For every remaining 5 potions, buy a 5-Bundle for 500 NC.
|
|
||||||
while num_items >= 5
|
|
||||||
nc_total += 500
|
|
||||||
summaries << "5-Bundle (500 NC)"
|
|
||||||
num_items -= 5
|
|
||||||
end
|
|
||||||
|
|
||||||
# For every remaining potion, buy each directly for 125 NC.
|
|
||||||
if num_items >= 1
|
|
||||||
nc_total += num_items * 125
|
|
||||||
summaries << "#{pluralize num_items, "potion"} (#{num_items * 125} NC)"
|
|
||||||
num_items = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
summaries << "0 NC" if summaries.empty?
|
|
||||||
summary = summaries.join(", ")
|
|
||||||
|
|
||||||
{nc_total:, summary:}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -260,5 +244,15 @@ module ItemsHelper
|
||||||
def item_header_user_lists_form_state
|
def item_header_user_lists_form_state
|
||||||
cookies.fetch("DTIItemPageUserListsFormState", "closed")
|
cookies.fetch("DTIItemPageUserListsFormState", "closed")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def outfit_viewer_layers(swf_assets)
|
||||||
|
swf_assets.map { |a| outfit_viewer_layer(a) }.join("\n").html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def outfit_viewer_layer(swf_asset)
|
||||||
|
content_tag "outfit-layer", style: "z-index: #{swf_asset.zone.depth}" do
|
||||||
|
image_tag swf_asset.image_url, alt: ""
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,2 @@
|
||||||
import React from "react";
|
// eslint-disable-next-line no-console
|
||||||
import ReactDOM from "react-dom";
|
console.log("OwO!");
|
||||||
|
|
||||||
import { AppProvider, ItemPageOutfitPreview } from "./wardrobe-2020";
|
|
||||||
|
|
||||||
const rootNode = document.querySelector("#outfit-preview-root");
|
|
||||||
const itemId = rootNode.getAttribute("data-item-id");
|
|
||||||
// TODO: Use the new React 18 APIs instead!
|
|
||||||
// eslint-disable-next-line react/no-deprecated
|
|
||||||
ReactDOM.render(
|
|
||||||
<AppProvider>
|
|
||||||
<ItemPageOutfitPreview itemId={itemId} />
|
|
||||||
</AppProvider>,
|
|
||||||
rootNode,
|
|
||||||
);
|
|
||||||
|
|
|
@ -17,6 +17,8 @@ class Item < ApplicationRecord
|
||||||
has_many :swf_assets, :through => :parent_swf_asset_relationships
|
has_many :swf_assets, :through => :parent_swf_asset_relationships
|
||||||
belongs_to :dyeworks_base_item, class_name: "Item",
|
belongs_to :dyeworks_base_item, class_name: "Item",
|
||||||
default: -> { inferred_dyeworks_base_item }, optional: true
|
default: -> { inferred_dyeworks_base_item }, optional: true
|
||||||
|
has_many :dyeworks_variants, class_name: "Item",
|
||||||
|
inverse_of: :dyeworks_base_item
|
||||||
|
|
||||||
|
|
||||||
attr_writer :current_body_id, :owned, :wanted
|
attr_writer :current_body_id, :owned, :wanted
|
||||||
|
@ -593,6 +595,10 @@ class Item < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def appearance_for(target, ...)
|
||||||
|
Item.appearances_for([id], target, ...)[id]
|
||||||
|
end
|
||||||
|
|
||||||
# Given a list of item IDs, return how they look on the given target (either
|
# Given a list of item IDs, return how they look on the given target (either
|
||||||
# a pet type or an alt style).
|
# a pet type or an alt style).
|
||||||
def self.appearances_for(item_ids, target, swf_asset_includes: [])
|
def self.appearances_for(item_ids, target, swf_asset_includes: [])
|
||||||
|
|
|
@ -70,19 +70,39 @@ class Item
|
||||||
match(DYEWORKS_LIMITED_FINAL_DATE_PATTERN)
|
match(DYEWORKS_LIMITED_FINAL_DATE_PATTERN)
|
||||||
return nil if match.nil?
|
return nil if match.nil?
|
||||||
|
|
||||||
# Parse this "<Month> <Day>" date as the *next* such date: parse it as
|
# Parse this "<Month> <Day>" date as the *next* such date, with some
|
||||||
# this year at first, then add a year if it turns out to be in the past.
|
# wiggle room for the possibility that it recently passed and Owls hasn't
|
||||||
|
# updated yet: parse it as this year at first, then add a year if that
|
||||||
|
# turns out to be more than 3 months ago. (That way, if it's currently
|
||||||
|
# December 2024, then events ending in Jan will be read as Jan 2025, and
|
||||||
|
# events ending in Nov will be read as Nov 2024.)
|
||||||
#
|
#
|
||||||
# NOTE: This could return strange results if the Owls date contains
|
# NOTE: This could return strange results if the Owls date contains
|
||||||
# something surprising! But the heuristic nature helps with e.g.
|
# something surprising! But the heuristic nature helps with e.g.
|
||||||
# flexibility if they abbreviate months, so let's lean into `Date.parse`.
|
# flexibility if they abbreviate months, so let's lean into `Date.parse`.
|
||||||
match => {month:, day:}
|
begin
|
||||||
date = Date.parse("#{month} #{day}, #{Date.today.year}")
|
match => {month:, day:}
|
||||||
date += 1.year if date < Date.today
|
date = Date.parse("#{month} #{day}, #{Date.today.year}")
|
||||||
|
date += 1.year if date < Date.today - 3.months
|
||||||
|
rescue Date::Error
|
||||||
|
Rails.logger.warn "Could not parse Dyeworks final date: " +
|
||||||
|
"#{nc_trade_value.value_text.inspect}"
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
date
|
date
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The probability of getting this item when dyeing the base item.
|
||||||
|
def dyeworks_odds
|
||||||
|
return nil unless dyeworks?
|
||||||
|
|
||||||
|
num_variants = dyeworks_base_item.dyeworks_variants.count
|
||||||
|
raise "Item's Dyeworks base has *no* variants??" if num_variants < 1
|
||||||
|
|
||||||
|
Rational(1, num_variants)
|
||||||
|
end
|
||||||
|
|
||||||
# Infer what base item this Dyeworks item probably relates to, based on
|
# Infer what base item this Dyeworks item probably relates to, based on
|
||||||
# their names. We only use this when a new item is modeled to initialize
|
# their names. We only use this when a new item is modeled to initialize
|
||||||
# the `dyeworks_base_item` relationship in the database; after that, we
|
# the `dyeworks_base_item` relationship in the database; after that, we
|
||||||
|
|
|
@ -13,7 +13,27 @@
|
||||||
how we handle zones. Until then, these items will be <em>very</em> buggy,
|
how we handle zones. Until then, these items will be <em>very</em> buggy,
|
||||||
sorry!
|
sorry!
|
||||||
|
|
||||||
#outfit-preview-root{'data-item-id': @item.id}
|
= turbo_frame_tag "item-preview" do
|
||||||
|
%outfit-viewer
|
||||||
|
%outfit-pet-appearance
|
||||||
|
= outfit_viewer_layers @pet_layers
|
||||||
|
%outfit-item-appearance
|
||||||
|
= outfit_viewer_layers @item_layers
|
||||||
|
|
||||||
|
= form_for item_path(@item), method: :get, class: "species-color-picker",
|
||||||
|
data: {"is-valid": @preview_error.nil?} do |f|
|
||||||
|
- if @preview_error == :pet_type_does_not_exist
|
||||||
|
%span.error-icon{title: "We haven't seen this kind of pet before."} ⚠️
|
||||||
|
- elsif @preview_error == :no_item_data
|
||||||
|
%span.error-icon{title: "We haven't seen this item on this pet before."} ⚠️
|
||||||
|
|
||||||
|
= select_tag "preview[color_id]",
|
||||||
|
options_from_collection_for_select(Color.funny.alphabetical,
|
||||||
|
"id", "human_name", @selected_preview_pet_type.color_id)
|
||||||
|
= select_tag "preview[species_id]",
|
||||||
|
options_from_collection_for_select(Species.alphabetical,
|
||||||
|
"id", "human_name", @selected_preview_pet_type.species_id)
|
||||||
|
= submit_tag "Go", name: nil
|
||||||
|
|
||||||
- unless @contributors_with_counts.empty?
|
- unless @contributors_with_counts.empty?
|
||||||
#item-contributors
|
#item-contributors
|
||||||
|
|
|
@ -11,17 +11,20 @@
|
||||||
[nc]: https://secure.nc.neopets.com/get-neocash
|
[nc]: https://secure.nc.neopets.com/get-neocash
|
||||||
[gc]: https://secure.nc.neopets.com/nickcash-cards
|
[gc]: https://secure.nc.neopets.com/nickcash-cards
|
||||||
|
|
||||||
%table.item-list
|
%table.item-list{"data-complexity": complexity_for(@items[:nc_mall])}
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
%th
|
%th.name-cell
|
||||||
Total: #{nc_total_for @items_needed[:nc_mall]} NC
|
Total: #{nc_total_for @items_needed[:nc_mall]} NC
|
||||||
(#{pluralize @items_needed[:nc_mall].size, "item"})
|
(#{pluralize @items_needed[:nc_mall].size, "item"})
|
||||||
|
|
||||||
%td.actions-cell
|
%td.actions-cell
|
||||||
- if @items_needed[:nc_mall].present?
|
- if @items_needed[:nc_mall].present?
|
||||||
%button{onclick: "alert('Todo!')"}
|
%button{
|
||||||
|
onclick: "alert('Todo!')",
|
||||||
|
data: {"action-kind": "bulk-nc-mall"},
|
||||||
|
}
|
||||||
= cart_icon alt: ""
|
= cart_icon alt: ""
|
||||||
Buy all in NC Mall
|
Buy all in NC Mall
|
||||||
%tbody
|
%tbody
|
||||||
|
@ -34,34 +37,41 @@
|
||||||
- if @items[:dyeworks].present?
|
- if @items[:dyeworks].present?
|
||||||
%h2 Dyeworks items
|
%h2 Dyeworks items
|
||||||
:markdown
|
:markdown
|
||||||
These are recolored "Dyeworks" variants of items. First get the "base"
|
These are recolored "Dyeworks" variants of items. Dyeworks is a game of
|
||||||
item, then get a Dyeworks Hue Brew Potion, and combine them in the
|
chance: if you have the "base" item and a Dyeworks Hue Brew Potion, you can
|
||||||
[Dyeworks][dyeworks] section of the NC Mall! Potions can also be bought in
|
[combine them][dyeworks] to receive a random color variant. You keep both
|
||||||
bundles of 5 or 10.
|
the new item *and* the base item.
|
||||||
|
|
||||||
|
If you don't get the color you want, you can use another potion to try
|
||||||
|
again. It's also common for users to exchange the variants they don't want
|
||||||
|
via "NC Trading". Potions can be bought individually, or in bundles of 5
|
||||||
|
or 10.
|
||||||
|
|
||||||
[dyeworks]: https://www.neopets.com/mall/dyeworks/
|
[dyeworks]: https://www.neopets.com/mall/dyeworks/
|
||||||
|
|
||||||
%table.item-list
|
%table.item-list{"data-complexity": complexity_for(@items[:dyeworks])}
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%td.thumbnail-cell
|
%td.thumbnail-cell
|
||||||
= image_tag "https://images.neopets.com/items/mall_80x80_cleaning.gif",
|
= image_tag "https://images.neopets.com/items/mall_80x80_cleaning.gif",
|
||||||
alt: "Dyeworks Hue Brew Potion"
|
alt: "Dyeworks Hue Brew Potion"
|
||||||
%th
|
%th.name-cell
|
||||||
Total: #{dyeworks_nc_total_for @items_needed[:dyeworks]} NC
|
Total: #{dyeworks_nc_total_for @items_needed[:dyeworks]} NC
|
||||||
= surround "(", ")" do
|
+
|
||||||
%span.price-breakdown{
|
%span.price-breakdown{
|
||||||
title: "#{dyeworks_items_nc_total_for(@items_needed[:dyeworks])} NC"
|
title: "At least #{pluralize @items_needed[:dyeworks].size, 'potion'}, " +
|
||||||
}<
|
"average " +
|
||||||
#{pluralize @items_needed[:dyeworks].size, "item"}
|
"#{dyeworks_average_num_potions_for @items_needed[:dyeworks]}, " +
|
||||||
+
|
"could be more. 125 NC per potion, but cheaper in bundles."
|
||||||
%span.price-breakdown{
|
}
|
||||||
title: dyeworks_potions_nc_summary(@items_needed[:dyeworks].size)
|
?? potions
|
||||||
}<
|
(~#{dyeworks_estimated_potions_cost_for @items_needed[:dyeworks]} NC)
|
||||||
#{pluralize @items_needed[:dyeworks].size, "potion"}
|
|
||||||
%td.actions-cell
|
%td.actions-cell
|
||||||
- if @items_needed[:dyeworks].present?
|
- if @items_needed[:dyeworks].present?
|
||||||
%button{onclick: "alert('Todo!')"}
|
%button{
|
||||||
|
onclick: "alert('Todo!')",
|
||||||
|
data: {"action-kind": "bulk-nc-mall"},
|
||||||
|
}
|
||||||
= cart_icon alt: ""
|
= cart_icon alt: ""
|
||||||
Buy all in NC Mall
|
Buy all in NC Mall
|
||||||
%tbody
|
%tbody
|
||||||
|
@ -70,36 +80,40 @@
|
||||||
- base_item = item.dyeworks_base_item
|
- base_item = item.dyeworks_base_item
|
||||||
|
|
||||||
- content_for :subtitle, flush: true do
|
- content_for :subtitle, flush: true do
|
||||||
= link_to base_item.name, base_item, target: "_blank"
|
= link_to base_item, target: "_blank" do
|
||||||
+ 1 potion
|
#{probability item.dyeworks_odds} chance
|
||||||
- if item.dyeworks_permanent?
|
- if item.dyeworks_permanent?
|
||||||
%span.dyeworks-timeframe{
|
%span.dyeworks-timeframe{
|
||||||
title: "This recipe is NOT currently scheduled to be removed " +
|
title: "This recipe is NOT currently scheduled to be removed " +
|
||||||
"from Dyeworks. It might not stay forever, but it's also " +
|
"from Dyeworks. It might not stay forever, but it's also " +
|
||||||
"not part of a known limited-time event, like most " +
|
"not part of a known limited-time event, like most " +
|
||||||
"Dyeworks items are. (Thanks Owls team!)"
|
"Dyeworks items are. (Thanks Owls team!)"
|
||||||
}
|
}
|
||||||
(Permanent)
|
(Always available)
|
||||||
- elsif item.dyeworks_limited_final_date.present?
|
- elsif item.dyeworks_limited_final_date.present?
|
||||||
%span.dyeworks-timeframe{
|
%span.dyeworks-timeframe{
|
||||||
title: "This recipe is part of a limited-time Dyeworks " +
|
title: "This recipe is part of a limited-time Dyeworks " +
|
||||||
"event. The last day you can dye this is " +
|
"event. The last day you can dye this is " +
|
||||||
"#{item.dyeworks_limited_final_date.to_fs(:month_and_day)}. " +
|
"#{item.dyeworks_limited_final_date.to_fs(:long)}. " +
|
||||||
"(Thanks Owls team!)"
|
"(Thanks Owls team!)"
|
||||||
}
|
}
|
||||||
(Thru #{item.dyeworks_limited_final_date.to_fs(:month_and_day)})
|
(Limited-time: #{item.dyeworks_limited_final_date.to_fs(:month_and_day)})
|
||||||
- elsif item.dyeworks_limited?
|
- elsif item.dyeworks_limited?
|
||||||
%span.dyeworks-timeframe{
|
%span.dyeworks-timeframe{
|
||||||
title: "This recipe is part of a limited-time Dyeworks " +
|
title: "This recipe is part of a limited-time Dyeworks " +
|
||||||
"event, and is scheduled to be removed from the NC Mall " +
|
"event, and is scheduled to be removed from the NC Mall " +
|
||||||
"soon. (Thanks Owls team!)"
|
"soon. (Thanks Owls team!)"
|
||||||
}
|
}
|
||||||
(Limited-time)
|
(Limited-time)
|
||||||
|
|
||||||
%button{onclick: "alert('Todo!')"}
|
%button{onclick: "alert('Todo!')"}
|
||||||
= cart_icon alt: ""
|
= cart_icon alt: ""
|
||||||
Buy base (#{item.dyeworks_base_item.current_nc_price} NC)
|
Buy base (#{item.dyeworks_base_item.current_nc_price} NC)
|
||||||
|
|
||||||
|
= button_link_to "NC Trades",
|
||||||
|
item_trades_path(item, type: "offering"),
|
||||||
|
target: "_blank", icon: search_icon
|
||||||
|
|
||||||
- if @items[:np].present?
|
- if @items[:np].present?
|
||||||
%h2 Neopoint items
|
%h2 Neopoint items
|
||||||
:markdown
|
:markdown
|
||||||
|
@ -112,11 +126,11 @@
|
||||||
[tp]: https://www.neopets.com/island/tradingpost.phtml?type=browse
|
[tp]: https://www.neopets.com/island/tradingpost.phtml?type=browse
|
||||||
[ag]: https://www.neopets.com/genie.phtml
|
[ag]: https://www.neopets.com/genie.phtml
|
||||||
|
|
||||||
%table.item-list
|
%table.item-list{"data-complexity": complexity_for(@items[:np])}
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
%th{colspan: 2}
|
%th.name-cell{colspan: 2}
|
||||||
Total: #{pluralize @items_needed[:np].size, "item"}
|
Total: #{pluralize @items_needed[:np].size, "item"}
|
||||||
%tbody
|
%tbody
|
||||||
- @items[:np].each do |item|
|
- @items[:np].each do |item|
|
||||||
|
@ -139,6 +153,7 @@
|
||||||
%table.item-list{
|
%table.item-list{
|
||||||
"data-group-type": "bundle",
|
"data-group-type": "bundle",
|
||||||
"data-group-owned": items.all?(&:owned?),
|
"data-group-owned": items.all?(&:owned?),
|
||||||
|
"data-complexity": complexity_for(items),
|
||||||
}
|
}
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
|
@ -195,10 +210,10 @@
|
||||||
|
|
||||||
[owls]: https://www.neopets.com/~owls
|
[owls]: https://www.neopets.com/~owls
|
||||||
|
|
||||||
%table.item-list
|
%table.item-list{"data-complexity": complexity_for(@items[:other_nc])}
|
||||||
%thead
|
%thead
|
||||||
%td
|
%td
|
||||||
%th{colspan: 2}
|
%th.name-cell{colspan: 2}
|
||||||
Total: #{pluralize @items_needed[:other_nc].size, "item"}
|
Total: #{pluralize @items_needed[:other_nc].size, "item"}
|
||||||
%tbody
|
%tbody
|
||||||
- @items[:other_nc].each do |item|
|
- @items[:other_nc].each do |item|
|
||||||
|
|
Loading…
Reference in a new issue