Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
bfe09ed
Add "product_grid_view" feature toggle
rioug May 22, 2026
951da2f
Inject "productGridViewFeature"
rioug May 22, 2026
bdd38c7
Add ProductsController
rioug May 22, 2026
8081f42
Disable product update from AngularJS when product grid view enabled
rioug May 22, 2026
4195d1a
Wire the productsController to the front end
rioug May 22, 2026
428bc4b
Made `products` public
rioug May 26, 2026
5ee736e
Load products
rioug May 26, 2026
e45a01a
Display products as a grid
rioug May 26, 2026
67316ea
Add product modal details
rioug May 27, 2026
834ddd4
Add partial for the product spinner
rioug May 27, 2026
4fd783f
Move the spinner styling to its own partial
rioug May 27, 2026
eaa22a4
Show loading spinner when changing order cycle
rioug May 27, 2026
9fdfa1a
Improve tooltip
rioug May 28, 2026
409e06a
Prettifying new scss file
rioug May 28, 2026
735477a
Clean up spec file
rioug May 28, 2026
6c101f0
Fix angular spec
rioug May 28, 2026
a3e46de
Per review, make spec more deterministic
rioug May 29, 2026
18fe7c6
Per review, spimplify code
rioug May 29, 2026
24f3fbe
Make the cache key feature aware
rioug May 30, 2026
30c7b79
Make sure we have product to hide before hiding
rioug May 30, 2026
dc30610
Per review, small fixes
rioug Jun 2, 2026
666f161
Per review, Simplify product grid view stimulus
rioug Jun 2, 2026
95ea333
Reduce column witdh
rioug Jun 3, 2026
17b9717
Add image title and alt
rioug Jun 3, 2026
dda80ef
Add ability to not have a link for tooltip
rioug Jun 3, 2026
a3843e8
Prettify css
rioug Jun 3, 2026
ae3651c
Remove link from propduct properties on product overlay
rioug Jun 3, 2026
42ab6c2
Remove `href` from span element
rioug Jun 5, 2026
ab3e1ac
Refactor AdminTooltipComponent
rioug Jun 12, 2026
8918645
Fix tooltip on enterprise use form
rioug Jun 12, 2026
dd274f9
Fix tooltip for product details and product preview
rioug Jun 12, 2026
443a512
Replace What's this? partial by a component
rioug Jun 14, 2026
b070c3a
Refactor AdminTooltipComponent to TooltipComponent
rioug Jun 14, 2026
a74fbb8
Fix typo
rioug Jun 15, 2026
65150ce
Remove link styling
rioug Jun 19, 2026
a997d0b
Use % for the modal max height
rioug Jun 19, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ angular.module('Darkswarm').controller "OrderCycleChangeCtrl", ($scope, $rootSco
Cart.reloadFinalisedLineItems()
ChangeableOrdersAlert.reload()
$rootScope.$broadcast 'orderCycleSelected'
event = new CustomEvent('orderCycleSelected')
event = new CustomEvent('orderCycleSelected', detail: { orderCycleId: $scope.order_cycle.order_cycle_id })
window.dispatchEvent(event)

$scope.closesInLessThan3Months = () ->
Expand Down
6 changes: 5 additions & 1 deletion app/assets/javascripts/darkswarm/services/products.js.coffee
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
angular.module('Darkswarm').factory 'Products', (OrderCycleResource, OrderCycle, Shopfront, currentHub, Dereferencer, Taxons, Properties, Cart, Variants) ->
angular.module('Darkswarm').factory 'Products', (OrderCycleResource, OrderCycle, Shopfront, currentHub, Dereferencer, Taxons, Properties, Cart, Variants, productGridViewFeature) ->
new class Products
constructor: ->
@update()
Expand All @@ -8,6 +8,10 @@ angular.module('Darkswarm').factory 'Products', (OrderCycleResource, OrderCycle,
loading: true

update: (params = {}, load_more = false) =>
if productGridViewFeature["enabled"] is true
console.warn "Products.update disabled..."
return

@loading = true
order_cycle_id = OrderCycle.order_cycle.order_cycle_id

Expand Down
2 changes: 2 additions & 0 deletions app/controllers/enterprises_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class EnterprisesController < BaseController
def shop
return redirect_to main_app.cart_path unless enough_stock?

@current_user = spree_current_user

set_noindex_meta_tag

@enterprise = current_distributor
Expand Down
7 changes: 7 additions & 0 deletions app/controllers/products_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class ProductsController < BaseController
def index
@order_cycle_id = params[:order_cycle_id]
end
end
10 changes: 10 additions & 0 deletions app/helpers/injection_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,16 @@ def inject_rails_flash
inject_json "railsFlash", OpenStruct.new(flash.to_hash), Api::RailsFlashSerializer
end

def inject_feature_flag
name = "productGridViewFeature"
json = if spree_current_user
{ enabled: feature?(:product_grid_view, spree_current_user) }.to_json
else
{ enabled: false }.to_json
end
Comment thread
dacook marked this conversation as resolved.
Outdated
render partial: "json/injection_ams", locals: { name:, json: }
end

def inject_json_array(name, data, serializer, opts = {})
opts = { each_serializer: serializer }.merge(opts)
serializer = ActiveModel::ArraySerializer
Expand Down
1 change: 1 addition & 0 deletions app/views/layouts/darkswarm.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
= inject_rails_flash
= inject_current_order
= inject_currency_config
= inject_feature_flag
= yield :injection_data


Expand Down
3 changes: 3 additions & 0 deletions app/views/products/index.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= turbo_frame_tag "shop-products" do
TODO Render products grid for order cycle
= @order_cycle_id
43 changes: 29 additions & 14 deletions app/views/shop/products/_form.html.haml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
- # NOTE: make sure that any changes in this template are reflected in app/views/admin/products_v3/product_preview.turbo_stream.haml
= cache_with_locale do
= cache_with_locale(current_order_cycle&.id) do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need to cache by feature toggle as well?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

we should, now that I think about it a bit more I am not sure the order cycle is needed but I'll double check that.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I reviewed the cache key and made it feature aware, see: 24f3fbe

%form{action: main_app.cart_path}
%products{"ng-init" => "refreshStaleData()", "ng-show" => "order_cycle.order_cycle_id != null", "ng-cloak" => true }

Expand All @@ -11,19 +11,34 @@
.medium-12.large-9.columns.full
= render partial: "shop/products/search_feedback"

.pad-top{ "infinite-scroll" => "loadMore()", "infinite-scroll-distance" => "1", "infinite-scroll-disabled" => 'Products.loading', "infinite-scroll-immediate-check": "false" }
%product.animate-repeat{"ng-controller" => "ProductNodeCtrl", "ng-repeat" => "product in Products.products track by product.id", "id" => "product-{{ product.id }}"}
= render "shop/products/summary"
.shop-variants
.variants.row{"ng-controller": "ShopVariantCtrl", variant: 'variant', "ng-repeat" => "variant in product.variants | orderBy: ['name_to_display','unit_value'] track by variant.id", "id" => "variant-{{ variant.id }}", "ng-class" => "{'out-of-stock': !variant.on_demand && variant.on_hand == 0}"}
= render "shop/products/shop_variant"
%product{"ng-show" => "Products.loading"}
.summary
.small-12.columns.text-center
= t :products_loading
.row.full
.small-12.columns.text-center
= render partial: "components/spinner"
- if feature?(:product_grid_view, @current_user)
- if current_order_cycle.present?
= turbo_frame_tag("shop-products", src: order_cycle_products_path(current_order_cycle.id), "data-controller": "product-grid-view", "data-action": "orderCycleSelected@window->product-grid-view#updateProducts") do
%product
.summary
.small-12.columns.text-center
= t :products_loading
.row.full
.small-12.columns.text-center
= render partial: "components/spinner"

- else
= turbo_frame_tag("shop-products", "data-controller": "product-grid-view", "data-action": "orderCycleSelected@window->product-grid-view#updateProducts") do
&nbsp;
- else
.pad-top{ "infinite-scroll" => "loadMore()", "infinite-scroll-distance" => "1", "infinite-scroll-disabled" => 'Products.loading', "infinite-scroll-immediate-check": "false" }
%product.animate-repeat{"ng-controller" => "ProductNodeCtrl", "ng-repeat" => "product in Products.products track by product.id", "id" => "product-{{ product.id }}"}
= render "shop/products/summary"
.shop-variants
.variants.row{"ng-controller": "ShopVariantCtrl", variant: 'variant', "ng-repeat" => "variant in product.variants | orderBy: ['name_to_display','unit_value'] track by variant.id", "id" => "variant-{{ variant.id }}", "ng-class" => "{'out-of-stock': !variant.on_demand && variant.on_hand == 0}"}
= render "shop/products/shop_variant"
%product{"ng-show" => "Products.loading"}
.summary
.small-12.columns.text-center
= t :products_loading
.row.full
.small-12.columns.text-center
= render partial: "components/spinner"

.hide-for-medium-down.large-1.columns
-# Space between products and filters
Expand Down
9 changes: 9 additions & 0 deletions app/webpacker/controllers/product_grid_view_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Controller } from "stimulus";

export default class extends Controller {
updateProducts(ev) {
const orderCycleId = ev.detail.orderCycleId;
// Updating the turbo-frame source will reload the frame
this.element.src = `/order_cycles/${orderCycleId}/products`;
}
Comment thread
dacook marked this conversation as resolved.
}
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
end
end

resources :order_cycles, only: [] do
resources :products, only: [:index]
end

namespace :stripe do
resources :callbacks, only: [:index]
resources :webhooks, only: [:create]
Expand Down
3 changes: 3 additions & 0 deletions lib/open_food_network/feature_toggle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ def self.conditional_features
"mo-dev" => <<~DESC,
Show DFC Permissions interface to share data with Market.Organic.
DESC
"product_grid_view" => <<~DESC,
Display shop products as a grid.
DESC
}.merge(conditional_features).freeze;

# Features you would like to be enabled to start with.
Expand Down
31 changes: 31 additions & 0 deletions spec/helpers/injection_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,35 @@
expect(injected_cards).to match "1234"
expect(injected_cards).not_to match "4321"
end

describe "#inject_feature_flag" do
subject(:feature_flag) { helper.inject_feature_flag }

let(:user) { create(:user) }

it "injects the produt grid feature flag" do
allow(helper).to receive(:spree_current_user).and_return user

expect(feature_flag).to match "productGridViewFeature"
expect(feature_flag).to match '"enabled":false'
end

context "with no user" do
it "injects a disabled product grid feature flag" do
allow(helper).to receive(:spree_current_user).and_return nil

expect(feature_flag).to match "productGridViewFeature"
expect(feature_flag).to match '"enabled":false'
end
end

context "with product grid feature enabled", feature: :product_grid_view do
it "injects an enabled product grid feature flag" do
allow(helper).to receive(:spree_current_user).and_return user

expect(feature_flag).to match "productGridViewFeature"
expect(feature_flag).to match '"enabled":true'
end
end
end
end