Skip to content

Commit

Permalink
Adds tag sorting capability and updates product displays
Browse files Browse the repository at this point in the history
  • Loading branch information
rosschapman committed May 8, 2024
1 parent 8e80021 commit baed227
Show file tree
Hide file tree
Showing 15 changed files with 174 additions and 22 deletions.
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
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!"
39 changes: 39 additions & 0 deletions app/furniture/marketplace/marketplace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,44 @@ def square_order_notifications_enabled?
def default_delivery_area
(delivery_areas.unarchived.size == 1) ? delivery_areas.unarchived.first : nil
end

# Because we've decided to make tagging or assigning to group optional for
# Marketplace Products, we need a method for querying these non-tagged
# Products for display
def products_with_no_group_tags
# A rough mental model of this query
#
# Step 1: Build a list of unarchived marketplace products represented by
# an array of boolean markers that map to the "is_group" column for each
# of their respective tags.
#
# | mp_tag_groups |
# | ------------- |
# | {t, f, t, f} | <-- has some group tags
# | {f, f} | <-- has no group tags
# | {t, t, t} | <-- has all group tags
#
# Step 2: Filter this list and return only the Marketplace Products who's
# corresonding row includes all `f`s.
with_no_group_tags = Product.find_by_sql <<-SQL.squish
select
mp.*
from
marketplace_products mp
full join marketplace_product_tags mpt on mpt.product_id = mp.id
full join marketplace_tags mt on mt.id = mpt.tag_id
where
mp.marketplace_id = '#{id}'
and mp.discarded_at is null
group by
mp.id
having not
't' = any(array_agg(mt.is_group));
SQL

# Step 3: Merge the above with all other products that are missing tags
# which won't be captured by the above query
with_no_group_tags + products.unarchived.where.missing(:tags)
end
end
end
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.ordered_tag_groups.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>Everything</h1>
<div class="grid lg:grid-cols-3 gap-3">
<%- marketplace.products_with_no_group_tags.each do |product| %>
<%= render Marketplace::Menu::ProductComponent.new(product:, cart:) %>
<%- end %>
</div>
5 changes: 5 additions & 0 deletions app/furniture/marketplace/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ class Tag < Record

attr_accessor :marketplace
location(parent: :marketplace)

positioned

scope :without_group, -> { where(is_group: false) }
scope :ordered_tag_groups, -> { where(is_group: true).merge(order(position: :asc, updated_at: :desc)) }
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 @@ -8,7 +8,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 section?" %>
<%= form.check_box :is_group %>
</div>
<%= form.submit %>
</div>
<%- end %>
9 changes: 9 additions & 0 deletions app/furniture/marketplace/tags/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<%
# We must assign the marketplace object explicitly in the current scope
# because of the funky way we relate Tags to the Marketplace through a Bazaar.
# Yet our routing and navigation depends on a `marketplace` object being present
# on a Tag.
mtag.marketplace = marketplace
%>
<%- breadcrumb :edit_marketplace_tag, mtag %>
<%= render "form", tag: mtag %>
51 changes: 41 additions & 10 deletions app/furniture/marketplace/tags/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,16 +1,47 @@
<%- breadcrumb :marketplace_tags, marketplace %>

<%= render CardComponent.new do |card| %>

<%- marketplace.tags.each do |tag| %>
<%- tag.marketplace = marketplace %>
<div id="<%= dom_id(tag)%>" class="flex flex-row gap-3">
<span class="flex-grow">
<%= tag.label %>
</span>
</div>
<h1>Product Groups</h1>
<p>Drag and drop to change the order in which groups will display on your Marketplace</p>
<ul data-controller="sortable" data-sortable-animation-value="150" data-sortable-resource-name-value="tag" class="flex flex-col gap-2 p-0">
<%- bazaar.tags.ordered_tag_groups.each do |tag| %>
<%-
# We must assign the marketplace object explicitly in the current scope
# because of the funky way we relate Tags to the Marketplace through a Bazaar.
# Yet our routing and navigation depends on a `marketplace` object being present
# on a Tag.
tag.marketplace = marketplace
%>
<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) %>
<%- if tag.is_group %>
<span class="inline-flex items-center text-xs justify-center px-1 text-xs bg-gray-200 rounded-full">
group
</span>
<% end %>
</li>
<%- end %>
<%- card.with_footer(variant: :action_bar) do %>
<%- new_tag = bazaar.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 %>
</ul>
<h1>Tags</h1>
<p>Add tags to label your products as vegan, discounted, etc...</p>
<%- bazaar.tags.without_group.each do |tag| %>
<%-
# We must assign the marketplace object explicitly in the current scope
# because of the funky way we relate Tags to the Marketplace through a Bazaar.
# Yet our routing and navigation depends on a `marketplace` object being present
# on a Tag.
tag.marketplace = marketplace
%>
<span 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">
<%= link_to tag.label, tag.location(:edit) %>
</span>
<%- end %>

<%- card.with_footer(variant: :action_bar) do %>
<%- new_tag = bazaar.tags.new %>
<%- if policy(new_tag).create? %>
Expand Down
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
redirect_to marketplace.location(child: :tags)
end
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);
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@
"@rails/actioncable": "^7.1.3",
"@rails/actiontext": "^7.1.3",
"@rails/activestorage": "^7.1.3",
"@rails/request.js": "^0.0.9",
"@sentry/browser": "^7.113.0",
"@stimulus-components/sortable": "^5.0.1",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.13",
"@webpack-cli/serve": "^2.0.5",
Expand All @@ -53,6 +55,7 @@
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-import": "^16.1.0",
"postcss-preset-env": "^9.5.9",
"sortablejs": "^1.15.2",
"tailwindcss": "^3.4.3",
"trix": "^2.1.1",
"webpack": "^5.76.0",
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
15 changes: 15 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,11 @@
dependencies:
spark-md5 "^3.0.1"

"@rails/request.js@^0.0.9":
version "0.0.9"
resolved "https://registry.yarnpkg.com/@rails/request.js/-/request.js-0.0.9.tgz#89e2a575405dc07eb8a9b3d2fe04289e1f057cd0"
integrity sha512-VleYUyrA3rwKMvYnz7MI9Ada85Vekjb/WVz7NuGgDO24Y3Zy9FFSpDMQW+ea/tlftD+CdX/W/sUosRA9/HkDOQ==

"@sentry-internal/[email protected]":
version "7.113.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.113.0.tgz#90a3c5493e289d589cfde79330fca549a24f41a4"
Expand Down Expand Up @@ -510,6 +515,11 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz#9cd84cc15bc865a5ca35fcaae198eb899f7b5c90"
integrity sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==

"@stimulus-components/sortable@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@stimulus-components/sortable/-/sortable-5.0.1.tgz#622b54bd11ab905ab28bdd8282399dc682d06933"
integrity sha512-03wQ+0fRaa0cYR3ia8+DJ4vW+UnAmBz24N8ChujWbceN79il49l7+hUZr74FNghmdX2OWhrmcr4YoG+VZ5XQ2g==

"@tailwindcss/forms@^0.5.7":
version "0.5.7"
resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.5.7.tgz#db5421f062a757b5f828bc9286ba626c6685e821"
Expand Down Expand Up @@ -2104,6 +2114,11 @@ slash@^5.0.0, slash@^5.1.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce"
integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==

sortablejs@^1.15.2:
version "1.15.2"
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.2.tgz#4e9f7bda4718bd1838add9f1866ec77169149809"
integrity sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA==

source-map-js@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
Expand Down

0 comments on commit baed227

Please sign in to comment.