Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Space admins can create "Menu/Product Groups" and reorder them for display on their marketplace #2361

Merged
merged 31 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
baed227
Adds tag sorting capability and updates product displays
rosschapman May 8, 2024
1ba0fea
Update app/furniture/marketplace/tags/_form.html.erb
rosschapman May 9, 2024
d7c5421
Improve naming, de-composition and testing of Marketplace::Tag scopes.
anaulin May 11, 2024
010a988
Replace `Marketplace.products_with_no_group_tags` with a scope on
anaulin May 11, 2024
cd5cd4b
Add tagged products to seeds' marketplace.
anaulin May 11, 2024
749db95
Fix lint issues.
anaulin May 11, 2024
7906242
Scopes tag position to bazaar
rosschapman May 14, 2024
616f72f
Merge branch 'main' into tag-sections
rosschapman May 14, 2024
f9372dc
Update app/furniture/marketplace/menu_component.html.erb
rosschapman May 14, 2024
c3a29f9
rolled back
rosschapman May 15, 2024
e7e23ba
Bump nokogiri from 1.16.4 to 1.16.5 (#2404)
dependabot[bot] May 14, 2024
71123b8
Bump postcss-preset-env from 9.5.11 to 9.5.13 (#2402)
dependabot[bot] May 14, 2024
2e89771
Bump aws-sdk-s3 from 1.149.1 to 1.150.0 (#2401)
dependabot[bot] May 14, 2024
b89747d
Remove obsolete docker-compose top-level `version` keyword (#2400)
anaulin May 14, 2024
4f70ea3
Bump square.rb from 37.0.0.20240417 to 38.0.0.20240515 (#2407)
dependabot[bot] May 15, 2024
27d5a36
Bump positioning from 0.2.1 to 0.2.2 (#2410)
dependabot[bot] May 21, 2024
fce6282
Bump rexml from 3.2.6 to 3.2.8 (#2409)
dependabot[bot] May 21, 2024
cdb734c
Bump standard from 1.35.1 to 1.36.0 (#2406)
dependabot[bot] May 21, 2024
161dedd
Bump aws-sdk-s3 from 1.150.0 to 1.151.0 (#2405)
dependabot[bot] May 21, 2024
97ff443
Bump rubocop-rails from 2.24.1 to 2.25.0 (#2411)
dependabot[bot] May 22, 2024
feb54e3
Bump selenium-webdriver from 4.20.1 to 4.21.1 (#2412)
dependabot[bot] May 22, 2024
aae219d
Removes Bazaar association from Tags (#2408)
rosschapman May 23, 2024
cf1e499
Runs yarn after merge
rosschapman May 23, 2024
7bdbd5b
Merge branch 'main' into tag-sections
rosschapman May 23, 2024
94ea17d
Updates styling and adds test placeholder
rosschapman May 23, 2024
a02cd6e
Adds specs
rosschapman May 23, 2024
c42c307
Update seeds
rosschapman May 23, 2024
2b9b494
Merge branch 'main' into tag-sections
rosschapman May 24, 2024
a4bae6b
Oops, add missing dep
rosschapman May 24, 2024
c3a496a
Updates spec
rosschapman May 24, 2024
e6e6b35
Merge branch 'main' into tag-sections
rosschapman May 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/components/svg_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,10 @@ def qr_code
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 6.75h.75v.75h-.75v-.75ZM6.75 16.5h.75v.75h-.75v-.75ZM16.5 6.75h.75v.75h-.75v-.75ZM13.5 13.5h.75v.75h-.75v-.75ZM13.5 19.5h.75v.75h-.75v-.75ZM19.5 13.5h.75v.75h-.75v-.75ZM19.5 19.5h.75v.75h-.75v-.75ZM16.5 16.5h.75v.75h-.75v-.75Z" />
SVG
end

def bars_3
<<~SVG
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
SVG
end
rosschapman marked this conversation as resolved.
Show resolved Hide resolved
end
5 changes: 5 additions & 0 deletions app/furniture/marketplace/breadcrumbs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@
link t("marketplace.tags.new.link_to"), marketplace.location(:new, child: :tag)
end

crumb :edit_marketplace_tag do |tag|
parent :marketplace_tags, tag.marketplace
link t("marketplace.tags.edit.link_to"), marketplace.location(:edit, child: :tag)
end

crumb :marketplace_tax_rates do |marketplace|
parent :edit_marketplace, marketplace
link t("marketplace.tax_rates.index.link_to"), marketplace.location(child: :tax_rates)
Expand Down
8 changes: 6 additions & 2 deletions app/furniture/marketplace/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ en:
link_to: "Payment Settings"
tags:
index:
link_to: "Tags"
link_to: "Product Tags"
new:
link_to: "Add Tag"
link_to: "Add Product Tag"
edit:
link_to: "Edit Product Tag"
update:
success: "Product Tag saved!"
17 changes: 14 additions & 3 deletions app/furniture/marketplace/menu_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-3">
<%- marketplace.products.with_all_rich_text.unarchived.each do |product| %>
<%= render Marketplace::Menu::ProductComponent.new(product:, cart:)%>
<%- marketplace.tags.group_tag.by_position.each do |tag| %>
<%- unless tag.products.empty? %>
<h1><%= tag.label %></h1>
<div class="grid lg:grid-cols-3 gap-3">
<%- tag.products.with_all_rich_text.unarchived.each do |product| %>
<%= render Marketplace::Menu::ProductComponent.new(product:, cart:) %>
<%- end %>
</div>
<%- end %>
<%- end %>
<h1>Other</h1>
<div class="grid lg:grid-cols-3 gap-3">
rosschapman marked this conversation as resolved.
Show resolved Hide resolved
rosschapman marked this conversation as resolved.
Show resolved Hide resolved
<%- marketplace.products.unarchived.without_group_tag.each do |product| %>
<%= render Marketplace::Menu::ProductComponent.new(product:, cart:) %>
<%- end %>
</div>
3 changes: 3 additions & 0 deletions app/furniture/marketplace/product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class Product < Record

before_commit :standardize_attachment_name

scope :with_group_tag, -> { joins(:tags).merge(Tag.group_tag) }
scope :without_group_tag, -> { where.not(id: with_group_tag) }
rosschapman marked this conversation as resolved.
Show resolved Hide resolved

def standardize_attachment_name
return unless photo.attached? && photo.blob.persisted?
return if photo.blob.filename.to_s.start_with?(space.id.to_s)
Expand Down
8 changes: 8 additions & 0 deletions app/furniture/marketplace/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,13 @@ class Tag < Record
validates :label, uniqueness: {case_sensitive: false, scope: :marketplace_id}

location(parent: :marketplace)

positioned on: :marketplace

rosschapman marked this conversation as resolved.
Show resolved Hide resolved
# Tacking `_tag` onto the end of this scope name solely to avoid
# collisions with ActiveRecord `groups`
scope :group_tag, -> { where(is_group: true) }
scope :not_group, -> { where(is_group: false) }
scope :by_position, -> { order(position: :asc) }
end
end
2 changes: 1 addition & 1 deletion app/furniture/marketplace/tag_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def space
end

def permitted_attributes(_params = nil)
%i[label]
%i[label is_group position]
end

def update?
Expand Down
12 changes: 8 additions & 4 deletions app/furniture/marketplace/tags/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<%= form_with(model: tag.location) do |form| %>

<%= render "text_field", attribute: :label, form: form %>

<%= form.submit %>
<div class="flex flex-col gap-5">
<div>
<%= render "text_field", attribute: :label, form: form %>
<%= form.label :is_group, "Is this a menu group?" %>
<%= form.check_box :is_group %>
</div>
<%= form.submit %>
</div>
<%- end %>
2 changes: 2 additions & 0 deletions app/furniture/marketplace/tags/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<%- breadcrumb :edit_marketplace_tag, mtag %>
<%= render "form", tag: mtag %>
48 changes: 36 additions & 12 deletions app/furniture/marketplace/tags/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
<%- breadcrumb :marketplace_tags, marketplace %>
<%= render CardComponent.new do |card| %>
<%- marketplace.tags.each do |tag| %>
<div id="<%= dom_id(tag)%>" class="flex flex-row gap-3">
<span class="flex-grow">
<%= tag.label %>
</span>
<div class="flex flex-col gap-4">
<div>
<h1>Menu Groups</h1>
<%- if marketplace.tags.group_tag.empty? %>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how in the "Tags" section there is a helpful message ("Adding tags to your products like "vegan" or "discounted" will help shoppers more easily find what they want"). Let's add something similar here, e.g: "Use product groups like "featured" or "produce" to display some of your products together, and to control the order in which products are displayed." (Or some better wording.)

There are currently no tags marked as Menu Groups
<% else %>
<p>Drag and drop to change the order in which groups will display on your Marketplace</p>
<ul data-tag-list-test data-controller="sortable" data-sortable-animation-value="150" data-sortable-resource-name-value="tag" class="flex flex-col gap-2 p-0">
<%- marketplace.tags.group_tag.by_position.each do |tag| %>
<li data-sortable-update-url="<%= polymorphic_path(tag.location) %>" id="<%= dom_id(tag)%>" class="items-center rounded-md bg-gray-50 px-2 py-1 font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 list-none cursor-move">
<%= render SvgComponent.new(icon: "bars_3", classes: "w-6 h-6 inline-block") %>
<%= link_to tag.label, tag.location(:edit) %>
</li>
<%- end %>
<%- end%>
</ul>
</div>
<%- end %>
<%- card.with_footer(variant: :action_bar) do %>
<%- new_tag = marketplace.tags.new %>
<%- if policy(new_tag).create? %>
<%= link_to t("marketplace.tags.new.link_to"), marketplace.location(:new, child: :tag), class: "button w-full" %>
<%- end %>
<%- end %>
<div>
<h1>Tags</h1>
<p>Adding tags to your products like "vegan" or "discounted" will help shoppers more easily find what they want</p>
<div class="flex space-x-2">
<%- marketplace.tags.not_group.each do |tag| %>
<span id="<%= dom_id(tag)%>" class="rounded-md bg-gray-50 px-2 py-1 font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10">
<%= link_to tag.label, tag.location(:edit) %>
</span>
<%- end %>
</div>
</div>
<div>
<%- card.with_footer(variant: :action_bar) do %>
<%- new_tag = marketplace.tags.new %>
<%- if policy(new_tag).create? %>
<%= link_to t("marketplace.tags.new.link_to"), marketplace.location(:new, child: :tag), class: "button w-full" %>
<%- end %>
<%- end %>
</div>
</div>
<%- end %>
18 changes: 18 additions & 0 deletions app/furniture/marketplace/tags_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ def new
authorize(mtag)
end

def edit
authorize(mtag)
end

def update
if authorize(mtag).update(mtag_params)
# POST requests to reorder tags (Stimulus Sortable) are sent with an
# html content-type so they cannot be handled by a JS responder.
if request.xhr?
render json: {}, status: :ok
else
rosschapman marked this conversation as resolved.
Show resolved Hide resolved
redirect_to marketplace.location(child: :tags)
end
rosschapman marked this conversation as resolved.
Show resolved Hide resolved
else
render :edit
end
end

def create
if authorize(mtag).save
redirect_to marketplace.location(child: :tags)
Expand Down
4 changes: 3 additions & 1 deletion app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// ./bin/rails generate stimulus controllerName

import { application } from "./application.js";

import MenuController from "./menu_controller.js";
import Sortable from "@stimulus-components/sortable";

application.register("menu", MenuController);
application.register("sortable", Sortable);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AddPositionIndexToMarketplaceTags < ActiveRecord::Migration[7.1]
disable_ddl_transaction!

def change
add_index :marketplace_tags, [:bazaar_id, :position], unique: true, algorithm: :concurrently
end
end
1 change: 1 addition & 0 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@
t.boolean "is_group", default: false, null: false
t.integer "position", default: 0, null: false
t.uuid "marketplace_id", null: false
t.index ["marketplace_id", "position"], name: "index_marketplace_tags_on_marketplace_id_and_position", unique: true
t.index ["marketplace_id"], name: "index_marketplace_tags_on_marketplace_id"
end

Expand Down
12 changes: 11 additions & 1 deletion db/seeds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
OPERATOR_EMAIL = "[email protected]"
MEMBER_EMAIL = "[email protected]"

raise "Your database contains data. Run `rails db:seed:replant` to truncate and re-seed it." if Space.exists?

FactoryBot.create(:person, :operator, name: "Ollie Operator", email: OPERATOR_EMAIL)

space = FactoryBot.create(:space, :with_members, :with_entrance, name: "Stevie's Space")
Expand All @@ -30,7 +32,15 @@
description: "A marvelous marketplace for magic merchandise.",
hero_image: FactoryBot.create(:media)
)
FactoryBot.create(:marketplace, :full, room: marketplace_section)
marketplace = FactoryBot.create(:marketplace, :ready_for_shopping, product_quantity: 16, room: marketplace_section)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code exists in main, and should not be appearing as a diff in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, it's not actually. You made these changes in this branch with cd5cd4b. ☺️

magic_menu_group = FactoryBot.create(:marketplace_tag, :group, marketplace: marketplace, label: "Magic")
fire_tag = FactoryBot.create(:marketplace_tag, marketplace: marketplace, label: "πŸ”₯")
marketplace.products.sample(8).each do |product|
product.tags << magic_menu_group
end
marketplace.products.sample(8).each do |product|
product.tags << fire_tag
end

journal_section = FactoryBot.create(
:room, space:, name: "Jazzy Journal",
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
"@rails/actioncable": "^7.1.3",
"@rails/actiontext": "^7.1.3",
"@rails/activestorage": "^7.1.3",
"@rails/request.js": "^0.0.9",
"@stimulus-components/sortable": "^5.0.1",
"@sentry/browser": "^8.3.0",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.13",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
FactoryBot.define do
factory :marketplace_tag, class: "Marketplace::Tag" do
marketplace { association(:marketplace) }
sequence(:label) { |n| "#{Faker::Food.allergen} #{n}" }
marketplace
sequence(:position) { |n| n }

trait :group do
is_group { true }
end
end
end
68 changes: 68 additions & 0 deletions spec/furniture/marketplace/marketplace_tags_system_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require "rails_helper"

describe "Marketplace Tags", type: :system do
let(:space) { create(:space, :with_entrance, :with_members) }
let(:marketplace) { create(:marketplace, :ready_for_shopping, room: space.entrance) }

before do
sign_in(space.members.first, space)
end

scenario "Add a marketplace tag" do
visit(marketplace)
click_link("Tags")
click_link("Add Product Tag")
fill_in("Label", with: "🚫🌾 Gluten Free")
click_button("Create")
expect(page).to have_content("🚫🌾 Gluten Free")
end

scenario "Edit a Marketplace Tag" do
visit(marketplace)
click_link("Tags")
click_link("Add Product Tag")
fill_in("Label", with: "🚫🌾 Gluten Free")
click_button("Create")
expect(page).to have_content("🚫🌾 Gluten Free")

click_link("🚫🌾 Gluten Free")
fill_in("Label", with: "🌾 Very Glutenous")
click_button("Save changes")
expect(page).to have_content("🌾 Very Glutenous")

click_link("🌾 Very Glutenous")
check("tag_is_group")
click_button("Save changes")
within("[data-tag-list-test]") do
expect(page).to have_content("🌾 Very Glutenous")
end
end

describe "Menu Groups" do
let!(:menu_tags) do
# The positioning gem won't let us manually assign positions on creation
create_list(:marketplace_tag, 3, :group, marketplace: marketplace).tap do |tags|
tags[0].update(position: :last)
tags[2].update(position: :first)
end
end

scenario "Displays Menu Groups in the correct order" do
visit(marketplace)
click_link("Tags")
within("[data-tag-list-test]") do
expect(page.find("li:nth-child(1)")).to have_content(menu_tags[2].label)
expect(page.find("li:nth-child(2)")).to have_content(menu_tags[1].label)
expect(page.find("li:nth-child(3)")).to have_content(menu_tags[0].label)
end
end
end

def visit(object_or_path)
if object_or_path.respond_to?(:location)
super(polymorphic_path(object_or_path.location))
else
super
end
end
end
36 changes: 36 additions & 0 deletions spec/furniture/marketplace/product_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,42 @@
it { is_expected.to have_many(:product_tax_rates).inverse_of(:product).dependent(:destroy) }
it { is_expected.to have_many(:tax_rates).through(:product_tax_rates).inverse_of(:products) }

describe ".with_group_tag" do
subject(:with_group_tag) { described_class.with_group_tag }

let!(:untagged_product) { create(:marketplace_product) }
let!(:product_with_non_group_tag) do
create(:marketplace_product).tap { |p| p.tags << create(:marketplace_tag) }
end
let!(:product_with_group_tag) do
create(:marketplace_product).tap { |p| p.tags << create(:marketplace_tag, :group) }
end

it "returns only products with a group tag" do
expect(with_group_tag).to include(product_with_group_tag)
expect(with_group_tag).not_to include(untagged_product)
expect(with_group_tag).not_to include(product_with_non_group_tag)
end
end

describe ".without_group_tag" do
subject(:without_group_tag) { described_class.without_group_tag }

let!(:untagged_product) { create(:marketplace_product) }
let!(:product_with_non_group_tag) do
create(:marketplace_product).tap { |p| p.tags << create(:marketplace_tag) }
end
let!(:product_with_group_tag) do
create(:marketplace_product).tap { |p| p.tags << create(:marketplace_tag, :group) }
end

it "returns only products without a group tag" do
expect(without_group_tag).to include(untagged_product)
expect(without_group_tag).to include(product_with_non_group_tag)
expect(without_group_tag).not_to include(product_with_group_tag)
end
end

describe "#name" do
it { is_expected.to validate_presence_of(:name) }
end
Expand Down
2 changes: 1 addition & 1 deletion spec/furniture/marketplace/product_tags_system_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
visit(marketplace)
click_link("Tags")

click_link("Add Tag")
click_link("Add Product Tag")

fill_in("Label", with: "🚫🌾 Gluten Free")

Expand Down
Loading
Loading