diff --git a/.vscode/ltex.dictionary.en-US.txt b/.vscode/ltex.dictionary.en-US.txt
new file mode 100644
index 000000000..52030509d
--- /dev/null
+++ b/.vscode/ltex.dictionary.en-US.txt
@@ -0,0 +1 @@
+Piikup
diff --git a/app/furniture/furniture.rb b/app/furniture/furniture.rb
index 6ffab535f..203aaf951 100644
--- a/app/furniture/furniture.rb
+++ b/app/furniture/furniture.rb
@@ -9,6 +9,7 @@ module Furniture
markdown_text_block: MarkdownTextBlock,
video_bridge: VideoBridge,
livestream: Livestream,
+ marketplace: Marketplace,
embedded_form: EmbeddedForm,
spotlight: Spotlight,
}.freeze
diff --git a/app/furniture/marketplace.rb b/app/furniture/marketplace.rb
new file mode 100644
index 000000000..86681b8d5
--- /dev/null
+++ b/app/furniture/marketplace.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+# Provids Vendors a location to sell their products
+class Marketplace
+ include Placeable
+
+ def self.append_routes(router)
+ router.namespace :marketplace do
+ router.resources :orders do
+ router.resources :items
+ router.resource :checkout
+ end
+ end
+ end
+
+ def delivery_fee=(delivery_fee)
+ settings['delivery_fee'] = delivery_fee
+ end
+
+ def delivery_fee
+ settings['delivery_fee']
+ end
+
+ def order_notification_email=(order_notification_email)
+ settings['order_notification_email'] = order_notification_email
+ end
+
+ def order_notification_email
+ settings['order_notification_email']
+ end
+
+ def stripe_account=(stripe_account)
+ settings['stripe_account'] = stripe_account
+ end
+
+ def stripe_account
+ settings['stripe_account']
+ end
+
+ def products
+ [{ name: "1lb of Bananas", price_cents: 1_39 }, { name: "32oz of Greek Yogurt", price_cents: 7_99 }].map do |product_attributes|
+ Marketplace::Product.new(product_attributes)
+ end
+ end
+
+ def order
+ orders.last || Marketplace::Order.create(location:placement)
+ end
+
+ def orders
+ Marketplace::Order.where(location: placement)
+ end
+
+ def attribute_names
+ super + %w[delivery_fee order_notification_email stripe_account]
+ end
+end
diff --git a/app/furniture/marketplace/_form.html.erb b/app/furniture/marketplace/_form.html.erb
new file mode 100644
index 000000000..10fb8a5ba
--- /dev/null
+++ b/app/furniture/marketplace/_form.html.erb
@@ -0,0 +1,5 @@
+<%= form.fields_for(:furniture) do |furniture_fields| %>
+ <%= render "text_field", attribute: :stripe_account, form: furniture_fields %>
+ <%= render "text_field", attribute: :delivery_fee, form: furniture_fields %>
+ <%= render "email_field", attribute: :order_notification_email, form: furniture_fields %>
+<%- end %>
\ No newline at end of file
diff --git a/app/furniture/marketplace/_in_room.html.erb b/app/furniture/marketplace/_in_room.html.erb
new file mode 100644
index 000000000..f837a1614
--- /dev/null
+++ b/app/furniture/marketplace/_in_room.html.erb
@@ -0,0 +1,19 @@
+<%- marketplace = furniture %>
+
+ <%- marketplace.products.each do |product| %>
+
+
<%= product.name %> (<%= product.price.format %>)
+
+ <%= button_to "Add to Order", [space, room, :furniture, marketplace.order, :items], params: { marketplace_item: { product: product.name } }, method: :post %>
+ <%- end %>
+
+
+
+
+
+
+
+
+ <%= link_to "Checkout", [space, room, :furniture, marketplace.order, :checkout], class: "button" %>
+
+
\ No newline at end of file
diff --git a/app/furniture/marketplace/checkout.rb b/app/furniture/marketplace/checkout.rb
new file mode 100644
index 000000000..46cfdb6a6
--- /dev/null
+++ b/app/furniture/marketplace/checkout.rb
@@ -0,0 +1,4 @@
+class Marketplace::Checkout
+ include ActiveModel::Model
+ attr_accessor :order
+end
\ No newline at end of file
diff --git a/app/furniture/marketplace/checkouts/_checkout.html.erb b/app/furniture/marketplace/checkouts/_checkout.html.erb
new file mode 100644
index 000000000..89a3fb359
--- /dev/null
+++ b/app/furniture/marketplace/checkouts/_checkout.html.erb
@@ -0,0 +1,4 @@
+
+
+
BOOGA BOGOA
+
\ No newline at end of file
diff --git a/app/furniture/marketplace/checkouts_controller.rb b/app/furniture/marketplace/checkouts_controller.rb
new file mode 100644
index 000000000..1aef5dc2e
--- /dev/null
+++ b/app/furniture/marketplace/checkouts_controller.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class Marketplace
+ class CheckoutsController < MarketplaceController
+ def show
+ render turbo_stream: turbo_stream.replace(dom_id(marketplace), checkout)
+ end
+
+ helper_method def checkout
+ order.checkout
+ end
+
+ helper_method def order
+ marketplace.orders.find(params[:order_id])
+ end
+ end
+end
diff --git a/app/furniture/marketplace/item.rb b/app/furniture/marketplace/item.rb
new file mode 100644
index 000000000..df930cdb0
--- /dev/null
+++ b/app/furniture/marketplace/item.rb
@@ -0,0 +1,23 @@
+class Marketplace::Item
+ include ActiveModel::Model
+
+ def id
+ @id ||= SecureRandom.uuid
+ end
+
+ def persisted?
+ true
+ end
+
+ attr_accessor :product, :order
+
+ delegate :price, to: :product
+
+ def save
+ order.save
+ end
+
+ def quantity
+ 1
+ end
+end
\ No newline at end of file
diff --git a/app/furniture/marketplace/item_policy.rb b/app/furniture/marketplace/item_policy.rb
new file mode 100644
index 000000000..7cf9290a7
--- /dev/null
+++ b/app/furniture/marketplace/item_policy.rb
@@ -0,0 +1,7 @@
+class Marketplace::ItemPolicy < ApplicationPolicy
+
+
+ def permitted_attributes(_params)
+ %i[product]
+ end
+end
\ No newline at end of file
diff --git a/app/furniture/marketplace/items/_item.html.erb b/app/furniture/marketplace/items/_item.html.erb
new file mode 100644
index 000000000..f9022a95f
--- /dev/null
+++ b/app/furniture/marketplace/items/_item.html.erb
@@ -0,0 +1,3 @@
+
+ <%= item.product.name %> x <%= item.quantity %> (<%= item.price.format %>)
+
\ No newline at end of file
diff --git a/app/furniture/marketplace/items_controller.rb b/app/furniture/marketplace/items_controller.rb
new file mode 100644
index 000000000..8e2df8505
--- /dev/null
+++ b/app/furniture/marketplace/items_controller.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+class Marketplace
+ class ItemsController < MarketplaceController
+ def create
+ item.save
+
+ render turbo_stream: turbo_stream
+ .append("#{dom_id(order)}-items", item)
+ end
+
+ private def item_params
+ policy(items.new).permit(params.require(:marketplace_item)).merge(order: order, product: product)
+ end
+
+ helper_method def item
+ @item ||= items.new(item_params)
+ end
+
+ helper_method def items
+ @items ||= order.items
+ end
+
+ helper_method def order
+ marketplace.orders.find(params[:order_id])
+ end
+
+ helper_method def product
+ marketplace.products.find { |p| p.name == params[:marketplace_item][:product] }
+ end
+ end
+end
diff --git a/app/furniture/marketplace/marketplace_controller.rb b/app/furniture/marketplace/marketplace_controller.rb
new file mode 100644
index 000000000..d4030cf6a
--- /dev/null
+++ b/app/furniture/marketplace/marketplace_controller.rb
@@ -0,0 +1,8 @@
+class Marketplace
+ class MarketplaceController < FurnitureController
+ # @returns [Marketplace]
+ helper_method def marketplace
+ room.furniture_placements.find_by(furniture_kind: 'marketplace').furniture
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/furniture/marketplace/order.rb b/app/furniture/marketplace/order.rb
new file mode 100644
index 000000000..9b64f6536
--- /dev/null
+++ b/app/furniture/marketplace/order.rb
@@ -0,0 +1,13 @@
+class Marketplace::Order < Item
+ def items
+ Marketplace::Item
+ end
+
+ def marketplace
+ furniture
+ end
+
+ def checkout
+ Marketplace::Checkout.new(order: self)
+ end
+end
\ No newline at end of file
diff --git a/app/furniture/marketplace/product.rb b/app/furniture/marketplace/product.rb
new file mode 100644
index 000000000..2533b4132
--- /dev/null
+++ b/app/furniture/marketplace/product.rb
@@ -0,0 +1,8 @@
+class Marketplace::Product
+ include ActiveModel::Model
+ attr_accessor :name, :price_cents
+
+ def price
+ Money.from_cents(price_cents, 'USD')
+ end
+end
diff --git a/features/furniture/delivery-order-form.feature.md b/features/furniture/delivery-order-form.feature.md
deleted file mode 100644
index 796ed5776..000000000
--- a/features/furniture/delivery-order-form.feature.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Feature: Furniture: Delivery Order Form
-
-Local Caterers and Delivery Companies partner to bring affordable, high-quality, local food right to your doorstep!
-
-This piece of furniture is for the Delivery Company to provide folks who are near-by that a local caterer is setting up;
-and if they want they can add their order and ?save some cash? (Need to check w/April re: most compelling hook).
-
-`@unimplemented-steps` `@unstarted`
-
-## Scenario: Offering a Delivery Order Form
-
-- Given a "Delivery Order Form" Furniture in the Entrance Hall to "Dev's Deliveries" Space
-- And a Space Member "dev@example.com" for "Dev's Deliveries" Space
-- And a Delivery Order Form Menu "Zee's Munchies Merchants" is in "Dev's Deliveries" Space with the following Menu Items:
- | name | price | description | photo |
- | A God Damn Pizza | $8.95 | It's a 12" margherita: Tomato's, Moz, Basil. WARNING: Does NOT travel well. No refunds. | |
- | Some Mother Effin' Granola| $2.99 | Maple syrup, dried cranberries, cinnamon, cardamom, almonds, and oats. What more can you want?! | |
-- When the following Delivery Order Form is opened in Dev's Deliveries" Space by the Space Member "dev@example.com":
- | date | 2022-03-05 |
- | delivery_window | 11AM to 1PM |
- | menu | Zee's Munchies Merchants |
-- Then a Delivery Order Form Order may be placed by Guests to the Entrance Hall to "Dev's Deliveries" Space
-
-`@unimplemented-steps` `@unstarted`
-
-## Scenario: Placing a Delivery Order Form Order
-
-- Given a "Delivery Order Form" Furniture in the Entrance Hall to "Dev's Deliveries" Space with the following configuration:
- | order_email | orders@devs-deliveries.example.com |
-- And a Delivery Order Form Menu "Zee's Munchies Merchants" is in "Dev's Deliveries" Space with the following Menu Items:
- | name | price | description | photo |
- | A God Damn Pizza | $8.95 | It's a 12" margherita: Tomato's, Moz, Basil. WARNING: Does NOT travel well. No refunds. | |
- | Some Mother Effin' Granola| $2.99 | Maple syrup, dried cranberries, cinnamon, cardamom, almonds, and oats. What more can you want?! | |
-- And the following Delivery Order Form is open in Dev's Deliveries" Space:
- | name | Downtown Oakland! Get Your Munchies! |
- | date | 2022-03-05 |
- | delivery_window | 11AM to 1PM |
- | menu | Zee's Munchies Merchants |
-- When the following Delivery Order Form Order is placed by the Guest "a-rando@example.com" in "Dev's Deliveries" Space:
- | popup | Downtown Oakland! Get Your Munchies! |
- | item[] | { "name": "A God Damn Pizza", price: "$8.95" } |
- | delivery_fee | $2.95 |
- | deliver_to | "123 N West St, Oakland, CA 94612 |
-- Then a Delivery Order Form Payment of $11.90 is collected from the Guest "A-rando@example.com" in "Dev's Deliveries" Space
-- And the following Delivery Order Form Order Confirmation Email is sent to "orders@devs-deliveries.example.com"
diff --git a/features/furniture/marketplace.feature.md b/features/furniture/marketplace.feature.md
new file mode 100644
index 000000000..17f7da272
--- /dev/null
+++ b/features/furniture/marketplace.feature.md
@@ -0,0 +1,36 @@
+# Feature: Furniture: Marketplace!
+
+Whether you're a farmers market or other local distributor, transactions between
+local vendors and the community is the cornerstone of a strong regional economy.
+
+A Marketplace connects producers, distributors and consumers in a single Space.
+## Scenario: Place a Delivery Order
+For now, our primary client is local owned and operated delivery
+organizations, like [Piikup](https://piikup.com/) or
+[Candlestick](https://www.candlestickcourier.com/).
+
+These organizations serve as distributors for regional vendors, closing the
+last-mile and keeping money flowing within the community.
+
+- Given a "Piikup Marketplace" Space
+- And a "Marketplace" Furniture in the Entrance Hall to "Piikup Marketplace" Space is configured with:
+ | delivery_fee | $6.99 |
+ | order_notification_email | orders@piikup.org |
+ | stripe_account | piikup-stripe-key |
+- And a Marketplace Vendor "Mandela Grocery" in the "Piikup Marketplace" Space has:
+ | stripe_account | mandela-stripe-key |
+ | order_notification_email | orders@mandelagrocery.coop |
+- And the Marketplace Vendor "Mandela Grocery" offers the following Products in the "Piikup Marketplace" Space
+ | name | price |
+ | 1lb of Bananas | $1.39 |
+ | 32oz of Greek Yogurt | $7.99 |
+- When a Guest places a Delivery Marketplace Order in the "Piikup Marketplace" Space for:
+ | item | quantity | price |
+ | 1lb of Bananas | 4 | $5.56 |
+ | 32oz of Greek Yogurt | 1 | $7.99 |
+ | Delivery by Piikup | 1 | $6.99 |
+- Then the Guest is charged $20.54 for their Marketplace Order
+- And the Marketplace Order placed by the Guest in the "Piikup Marketplace" Space is delivered to "orders@piikup.org"
+- And the Stripe Account "piikup-stripe-key" receives a Payment of $6.99
+- And the Marketplace Order placed by the Guest in the "Piikup Marketplace" Space is delivered to "orders@mandelagrocery.coop"
+- And the Stripe Account "mandela-stripe-key" receives a Payment of $13.55
diff --git a/features/steps/furniture/marketplace_steps.js b/features/steps/furniture/marketplace_steps.js
new file mode 100644
index 000000000..fb807c7b1
--- /dev/null
+++ b/features/steps/furniture/marketplace_steps.js
@@ -0,0 +1,31 @@
+import { Given, When, Then } from "@cucumber/cucumber";
+Given('{a} Marketplace Vendor {string} in {a} {space} has:', function (a, string, a2, space, dataTable) {
+ // Write code here that turns the phrase above into concrete actions
+ return 'pending';
+});
+
+
+Given('{a} Marketplace Vendor {string} offers {a} following Products in {a} {space}', function (a, string, a2, a3, space, dataTable) {
+ // Write code here that turns the phrase above into concrete actions
+ return 'pending';
+});
+
+When('{a} {actor} places {a} Delivery Marketplace Order in {a} {space} for:', function (a, actor, a2, a3, space, dataTable) {
+ // Write code here that turns the phrase above into concrete actions
+ return 'pending';
+});
+
+Then('{a} Marketplace Order placed by {a} {actor} in {a} {space} is delivered to {string}', function (a, a2, actor, a3, space, string) {
+ // Write code here that turns the phrase above into concrete actions
+ return 'pending';
+});
+
+Then('{a} {actor} is charged ${float} for their Marketplace Order', function (a, actor, float) {
+ // Write code here that turns the phrase above into concrete actions
+ return 'pending';
+});
+
+Then('{a} Stripe Account {string} receives {a} Payment of ${float}', function (a, string, a2, float) {
+ // Write code here that turns the phrase above into concrete actions
+ return 'pending';
+});
\ No newline at end of file