Skip to content

Commit

Permalink
Marketplace: Buyer adds Product to Order (#862)
Browse files Browse the repository at this point in the history
* Marketplace: Products relate to a Marketplace, not a Space.
* Marketplace: Rooms can have many marketplaces
* Furniture: Assume a `no-op` form if `form_template` is not overridden

Co-authored-by: Kelly Hong <[email protected]>
Co-authored-by: Ana Ulin <[email protected]>

* Marketplace: Buyer may add, update, and remove Products to Order!
* Marketplace Separate order flow from the Product management flow.
* Marketplace: Label Add a Product button + Style Better
* Marketplace: Remove Product Button Removes Product!
* Marketplace: Rename from Order to Cart
* Marketplace: hide the `-` button when product not in cart
* Marketplace: Improve layout and phrasing on Cart
* Marketplace: Turbo-streamify changing Cart Product's Quantity
* Marketplace: Turn on turbo when using the minus buttonom>
* Marketplace: More tidying of the - 0 + qty picker
* Marketplace: Always show cents


Co-authored-by: Kelly <[email protected]>
Co-authored-by: Zee <[email protected]>
Co-authored-by: Ana <[email protected]>
Co-authored-by: Kelly Hong <[email protected]>
Co-authored-by: Neermala Thapa <[email protected]>
Co-authored-by: Naomi Quinones <[email protected]>
Co-authored-by: Erica Hagle <[email protected]>
Co-authored-by: Zee Spencer <[email protected]>
Co-authored-by: Ana Ulin <[email protected]>
  • Loading branch information
10 people authored Nov 10, 2022
1 parent 6f9f0f9 commit 2c44d95
Show file tree
Hide file tree
Showing 36 changed files with 488 additions and 70 deletions.
2 changes: 2 additions & 0 deletions app/furniture/journal/entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class Journal::Entry < ApplicationRecord

attribute :published_at, :datetime

# @!attribute journal
# @return [Journal::Journal]
belongs_to :journal, class_name: "Journal::Journal", inverse_of: :entries
delegate :room, :space, to: :journal

Expand Down
11 changes: 7 additions & 4 deletions app/furniture/marketplace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ class Marketplace
include Placeable

def self.append_routes(router)
router.resource :marketplace, only: [] do
router.resources :products, only: %i[new create index], module: "marketplace"
router.resources :marketplaces, only: [:show], module: "marketplace" do
router.resources :products
router.resources :carts do
router.resources :cart_products
end
end
end

def products
Marketplace::Product.where(space: space)
def self.from_placement(placement)
placement.becomes(Marketplace)
end
end
1 change: 0 additions & 1 deletion app/furniture/marketplace/_marketplace.html.erb

This file was deleted.

28 changes: 28 additions & 0 deletions app/furniture/marketplace/breadcrumbs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

# @see https://github.com/kzkn/gretel

crumb :marketplace do |marketplace|
parent :room, marketplace.room
link 'Marketplace', marketplace.location
end

crumb :marketplace_products do |marketplace|
parent :marketplace, marketplace
link 'Products', marketplace.location(:products)
end

crumb :marketplace_product do |product|
parent :marketplace_products, product.marketplace
link product.name, product.location
end

crumb :new_marketplace_product do |product|
parent :marketplace_products, product.marketplace
link 'Add a Product', [:new] + marketplace.location(:product)
end

crumb :edit_marketplace_product do |product|
parent :marketplace_product, product
link 'Edit', [:edit] + product.location
end
16 changes: 16 additions & 0 deletions app/furniture/marketplace/cart.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

class Marketplace
class Cart < ApplicationRecord
self.table_name = 'marketplace_carts'

belongs_to :marketplace
delegate :space, :room, to: :marketplace
has_many :cart_products
has_many :products, through: :cart_products

def self.model_name
@_model_name ||= ActiveModel::Name.new(self, ::Marketplace)
end
end
end
17 changes: 17 additions & 0 deletions app/furniture/marketplace/cart_product.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

class Marketplace
class CartProduct < ApplicationRecord
self.table_name = "marketplace_cart_products"

belongs_to :cart, inverse_of: :cart_products
belongs_to :product, inverse_of: :cart_products
validates_uniqueness_of :product, scope: :cart_id

attribute :quantity, :integer, default: 0

def self.model_name
@_model_name ||= ActiveModel::Name.new(self, ::Marketplace)
end
end
end
33 changes: 33 additions & 0 deletions app/furniture/marketplace/cart_products/_cart_product.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<%- product = cart_product.product %>
<%- cart = cart_product.cart %>
<tr id="cart-product-<%= cart_product.product_id %>">
<td class="w-full max-w-0 py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:w-auto sm:max-w-none sm:pl-6">
<%= product.name %>
<dl class="font-normal lg:hidden">
<dt class="sr-only"><%= product.class.human_attribute_name(:price) %></dt>
<dd><%= humanized_money_with_symbol(product.price) %></dd>
<dt class="sr-only"><%= product.class.human_attribute_name(:description) %></dt>
<dd class="mt-1 truncate text-gray-700"><%= product.description %></dd>

</dl>
</td>
<td class="hidden px-3 py-4 text-sm text-gray-500 lg:table-cell">
<%= product.description %>
</td>
<td class="hidden px-3 py-4 text-sm text-gray-500 sm:table-cell">
<%= humanized_money_with_symbol(product.price) %>
</td>
<td class="py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 flex flex-row justify-between items-center">
<%# TODO: Extract minus and plus buttons into components %>
<%- minus_quantity = [cart_product.quantity - 1, 0].max %>
<%- minus_method = minus_quantity.zero? ? :delete : :put %>
<%= render "buttons/minus", disabled: cart_product.quantity.zero?, method: minus_method, title: t('.remove'), disabled: cart_product.quantity.zero?, href: [cart.space, cart.room, cart.marketplace, cart, cart_product, { cart_product: { quantity: minus_quantity, product_id: product.id} }] %>


<span class="py-2 px-2 my-1 text-lg"><%= cart_product.quantity %></span>

<%- add_quantity = cart_product.quantity + 1 %>
<%- add_method = add_quantity == 1 ? :post : :put %>
<%= render "buttons/plus", method: add_method, title: t('.add'), href: [cart.space, cart.room, cart.marketplace, cart, cart_product, { cart_product: {quantity: add_quantity, product_id: product.id} }] %>
</td>
</tr>
91 changes: 91 additions & 0 deletions app/furniture/marketplace/cart_products_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
class Marketplace
class CartProductsController < FurnitureController
def create
cart_product.save

respond_to do |format|
format.html do
if cart_product.errors.empty?
flash[:notice] = t(".success",
product: cart_product.product.name.pluralize(cart_product.quantity),
quantity: cart_product.quantity)
else
flash[:alert] = t(".failure",
product: cart_product.product.name.pluralize(cart_product.quantity),
quantity: cart_product.quantity)
end

redirect_to [marketplace.space, marketplace.room]
end

format.turbo_stream do
render turbo_stream: turbo_stream.replace("cart-product-#{cart_product.product_id}", cart_product)
end
end
end

def update
cart_product.update(cart_product_params)
respond_to do |format|
format.html do
if cart_product.errors.empty?
flash[:notice] =
t(".success", product: cart_product.product.name.pluralize(cart_product.quantity),
quantity: cart_product.quantity)
else
flash[:alert] =
t(".failure", product: cart_product.product.name.pluralize(cart_product.quantity),
quantity: cart_product.quantity)
end

redirect_to [marketplace.space, marketplace.room]
end
format.turbo_stream do
render turbo_stream: turbo_stream.replace("cart-product-#{cart_product.product_id}", cart_product)
end
end
end

def destroy
cart_product.destroy
respond_to do |format|
format.html do
if cart_product.destroyed?
flash[:notice] =
t(".success", product: cart_product.product.name.pluralize(cart_product.quantity),
quantity: cart_product.quantity)
else
flash[:alert] =
t(".failure", product: cart_product.product.name.pluralize(cart_product.quantity),
quantity: cart_product.quantity)
end
redirect_to [marketplace.space, marketplace.room]
end

format.turbo_stream do
render turbo_stream: turbo_stream.replace("cart-product-#{cart_product.product_id}", cart.cart_products.new(product: cart_product.product))
end
end
end

def cart_product
@cart_product ||= if params[:id]
cart.cart_products.find(params[:id])
elsif params[:cart_product]
cart.cart_products.new(cart_product_params)
end
end

def cart
marketplace.carts.find(params[:cart_id])
end

def marketplace
room.furniture_placements.find(params[:marketplace_id]).furniture
end

def cart_product_params
params.require(:cart_product).permit(:quantity, :product_id)
end
end
end
21 changes: 21 additions & 0 deletions app/furniture/marketplace/locales/en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
en:
marketplace:
products:
index:
new: Add a Product
product:
add: Add Product
remove: Remove Product
cart_products:
cart_product:
remove: Remove from Cart
add: Add to Cart
destroy:
success: "Removed %{quantity} %{product} from Cart"
failure: "Could not remove %{quantity} %{product} from Cart"
create:
success: "Added %{quantity} %{product} to Cart"
failure: "Could not add %{quantity} %{product} to Cart"
update:
success: "Changed %{quantity} %{product} on Cart"
failure: "Could not change %{quantity} %{product} on Cart"
16 changes: 16 additions & 0 deletions app/furniture/marketplace/marketplace.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

class Marketplace
class Marketplace < FurniturePlacement
has_many :products, inverse_of: :marketplace
has_many :carts, inverse_of: :marketplace

def self.model_name
@_model_name ||= ActiveModel::Name.new(self, ::Marketplace)
end

def location(child = nil)
[space, room, self, child].compact
end
end
end
26 changes: 0 additions & 26 deletions app/furniture/marketplace/marketplace/_marketplace.html.erb

This file was deleted.

30 changes: 30 additions & 0 deletions app/furniture/marketplace/marketplaces/_marketplace.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

<div class="-mx-4 mt-8 overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:-mx-6 md:mx-0 md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300 table-fixed">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
<%= Marketplace::Product.human_attribute_name(:name) %>
</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">
<%= Marketplace::Product.human_attribute_name(:description) %>
</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:table-cell">
<%= Marketplace::Product.human_attribute_name(:price) %>
</th>
<th scope="col" class="w-48">&nbsp;</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
<%- cart = marketplace.carts.find_or_create_by(id: session[:current_cart] ||= SecureRandom.uuid) %>
<%- marketplace.products.each do |product| %>
<%= render cart.cart_products.find_or_initialize_by(product: product) %>
<%- end %>
</tbody>
</table>
</div>
<div class="m-2 text-center">
<%- if policy(marketplace.products.new).create? %>
<%= link_to t('.manage_products'), marketplace.location(:products) %>
<%- end %>
</div>
2 changes: 2 additions & 0 deletions app/furniture/marketplace/marketplaces/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<%- breadcrumb :marketplace, marketplace %>
<%= render marketplace %>
11 changes: 11 additions & 0 deletions app/furniture/marketplace/marketplaces_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class Marketplace
class MarketplacesController < FurnitureController
def show; end

helper_method def marketplace
Marketplace.find(params[:id])
end
end
end
15 changes: 13 additions & 2 deletions app/furniture/marketplace/product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,25 @@

class Marketplace
class Product < ApplicationRecord
self.table_name = "marketplace_products"
belongs_to :space
self.table_name = 'marketplace_products'

extend StripsNamespaceFromModelName

belongs_to :marketplace
delegate :space, to: :marketplace

has_many :cart_products, inverse_of: :product
has_many :carts, through: :cart_products

attribute :name, :string
validates :name, presence: true

attribute :description, :string

monetize :price_cents

def location
marketplace.location(self)
end
end
end
9 changes: 9 additions & 0 deletions app/furniture/marketplace/product_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@

class Marketplace
class ProductPolicy < ApplicationPolicy
alias product object
def permitted_attributes(_params)
%i[name description price_cents price_currency]
end

def create?
person.member_of?(product.space)
end

def update?
create?
end
end
end
6 changes: 6 additions & 0 deletions app/furniture/marketplace/products/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<%= form_with model: product.location do |f| %>
<%= render "text_field", { attribute: :name, form: f} %>
<%= render "text_area", { attribute: :description, form: f} %>
<%= render "number_field", { attribute: :price_cents, form: f} %>
<%= f.submit %>
<% end %>
Loading

0 comments on commit 2c44d95

Please sign in to comment.