diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a965d6813..fafc17e5c 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/devcontainers/ruby:3.2 +FROM mcr.microsoft.com/devcontainers/ruby:3.3 LABEL org.opencontainers.image.source=https://github.com/zinc-collective/convene-devcontainer LABEL org.opencontainers.image.description="Container to facilitate easier development of convene, specifically to work in GitHub CodeSpaces." diff --git a/.gitignore b/.gitignore index 753b67f2e..7a316168d 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,8 @@ app/assets/builds/* .devcontainer/output -TAGS +# # Ignore Bust A Gem VSCode extension ctags generation. +/TAGS + +# Webpack Bundle Analyzer +webpack-stats.json diff --git a/.rubocop.yml b/.rubocop.yml index fedd29ec9..db04b00a7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -18,6 +18,12 @@ AllCops: - vendor/bundle/**/* NewCops: enable + +RSpec/Capybara/FeatureMethods: + Exclude: + - '**/*_system_spec.rb' + - spec/system/**/*_spec.rb + RSpec/MultipleExpectations: Enabled: false @@ -29,6 +35,10 @@ RSpec/NestedGroups: RSpec/ExampleLength: Max: 10 + Exclude: + - '**/*_system_spec.rb' + - spec/system/**/*_spec.rb + Style/TrailingCommaInArrayLiteral: Exclude: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 915b0b212..3a30e0858 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 2000` -# on 2023-09-14 00:31:11 UTC using RuboCop version 1.56.3. +# on 2024-03-07 02:44:05 UTC using RuboCop version 1.61.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -32,12 +32,6 @@ RSpec/DescribeClass: Exclude: - 'spec/tasks/release_after_build_spec.rb' -# Offense count: 2 -# Configuration parameters: Max, CountAsOne. -RSpec/ExampleLength: - Exclude: - - 'spec/requests/rsvps_controller_request_spec.rb' - # Offense count: 1 RSpec/LeakyConstantDeclaration: Exclude: @@ -57,14 +51,7 @@ RSpec/NamedSubject: - 'spec/models/invitation_spec.rb' - 'spec/models/membership_spec.rb' -# Offense count: 1 -# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. -# Include: **/*_spec.rb -RSpec/SpecFilePathFormat: - Exclude: - - 'spec/furniture/journal/entries_request_spec.rb' - -# Offense count: 8 +# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: slashes, arguments @@ -73,7 +60,6 @@ Rails/FilePath: - 'config/application.rb' - 'config/environments/development.rb' - 'config/initializers/gretel.rb' - - 'spec/rails_helper.rb' # Offense count: 1 # Configuration parameters: Include. diff --git a/.ruby-version b/.ruby-version index b347b11ea..15a279981 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.2.3 +3.3.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 23e38e15d..4ba7360b4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -66,8 +66,11 @@ First, ensure your development environment has: 1. Ruby (See [.ruby-version](./.ruby-version) for version) 2. Node (See [.nvmrc](./.nvmrc) for version) 3. [Yarn] -4. [PostgreSQL]. (Note: For people using [Docker], a [docker-compose.yml] - file has been included for convenience.) +4. [PostgreSQL] +5. [Redis] + +(Note: If you use [Docker], a [docker-compose.yml] file has been included with + [PostgreSQL] and [Redis] for convenience.) Then, run `bin/setup` to install Ruby and Node dependencies and set up the database. @@ -83,7 +86,8 @@ Log in with space-member@example.com. You can check the email using http://local Finally, with the server still running (perhaps in a different terminal), run `bin/test` to ensure that your development environment is configured correctly. -[PostgreSQL 12]: https://www.postgresql.org/download/ +[PostgreSQL]: https://www.postgresql.org/ +[Redis]: https://redis.io/ [Docker]: https://www.docker.com [docker-compose.yml]: ./docker-compose.yml [.env.development.example]: ./.env.development.example diff --git a/Gemfile b/Gemfile index e3399a2a9..fad2fe698 100644 --- a/Gemfile +++ b/Gemfile @@ -64,7 +64,7 @@ gem "friendly_id", "~> 5.5.1" gem "bcrypt", "~> 3.1.20" gem "lockbox", "1.3.3" gem "rotp", "~> 6.3" -gem "strong_migrations", "~> 1.7" +gem "strong_migrations", "~> 1.8" # Soft Deletion gem "discard", "~> 1.2" @@ -78,7 +78,7 @@ gem "pg", "~> 1.5" gem "image_processing" # Use S3 for file storage -gem "aws-sdk-s3", "~> 1.143", require: false +gem "aws-sdk-s3", "~> 1.146", require: false # Date/Time and Internationalization # # Windows does not include zoneinfo files, so bundle the tzinfo-data gem @@ -117,9 +117,9 @@ group :development, :test do # Our preferred testing library for Ruby and Rails projects gem "rails-controller-testing" - gem "rspec-rails", "~> 6.1.1" + gem "rspec-rails", "~> 6.1.2" gem "rswag-specs" - gem "shoulda-matchers", "~> 6.1" + gem "shoulda-matchers", "~> 6.2" gem "capybara" gem "selenium-webdriver" @@ -129,7 +129,7 @@ group :development, :test do gem "rubocop-rails" gem "rubocop-rspec" - gem "standard", "~> 1.34" + gem "standard", "~> 1.35" end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 3f9c58b0a..86d424df0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -108,16 +108,16 @@ GEM faraday-retry (~> 2.0) ast (2.4.2) aws-eventstream (1.3.0) - aws-partitions (1.883.0) - aws-sdk-core (3.191.0) + aws-partitions (1.899.0) + aws-sdk-core (3.191.4) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.77.0) + aws-sdk-kms (1.78.0) aws-sdk-core (~> 3, >= 3.191.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.143.0) + aws-sdk-s3 (1.146.0) aws-sdk-core (~> 3, >= 3.191.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) @@ -129,10 +129,10 @@ GEM erubi (>= 1.0.0) rack (>= 0.9.0) rouge (>= 1.0.0) - bigdecimal (3.1.6) + bigdecimal (3.1.7) bindex (0.8.1) - binding_of_caller (1.0.0) - debug_inspector (>= 0.0.1) + binding_of_caller (1.0.1) + debug_inspector (>= 1.2.0) bootsnap (1.18.3) msgpack (~> 1.2) builder (3.2.4) @@ -161,19 +161,18 @@ GEM cssbundling-rails (1.4.0) railties (>= 6.0.0) date (3.3.3) - debug_inspector (1.1.0) + debug_inspector (1.2.0) decent_exposure (3.0.4) activesupport (>= 4.0) - diff-lcs (1.5.0) + diff-lcs (1.5.1) discard (1.3.0) activerecord (>= 4.2, < 8) docile (1.4.0) - dotenv (2.8.1) - dotenv-rails (2.8.1) - dotenv (= 2.8.1) - railties (>= 3.2) - drb (2.2.0) - ruby2_keywords + dotenv (3.1.0) + dotenv-rails (3.1.0) + dotenv (= 3.1.0) + railties (>= 6.1) + drb (2.2.1) erubi (1.12.0) factory_bot (6.4.5) activesupport (>= 5.0.0) @@ -209,9 +208,9 @@ GEM actionview (>= 5.1, < 7.2) railties (>= 5.1, < 7.2) hashdiff (1.1.0) - htmlbeautifier (1.4.2) + htmlbeautifier (1.4.3) htmlentities (4.3.4) - i18n (1.14.1) + i18n (1.14.4) concurrent-ruby (~> 1.0) i18n-debug (1.2.0) i18n (< 2) @@ -219,7 +218,7 @@ GEM mini_magick (>= 4.9.5, < 5) ruby-vips (>= 2.0.17, < 3) io-console (0.7.2) - irb (1.11.2) + irb (1.12.0) rdoc reline (>= 0.4.2) jbuilder (2.11.5) @@ -240,7 +239,7 @@ GEM loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) - lookbook (2.2.1) + lookbook (2.2.2) activemodel css_parser htmlbeautifier (~> 1.3) @@ -250,20 +249,20 @@ GEM redcarpet (~> 3.5) rouge (>= 3.26, < 5.0) view_component (>= 2.0) - yard (~> 0.9.25) + yard (~> 0.9) zeitwerk (~> 2.5) mail (2.8.1) mini_mime (>= 0.1.1) net-imap net-pop net-smtp - marcel (1.0.2) + marcel (1.0.4) matrix (0.4.2) method_source (1.0.0) mini_magick (4.12.0) mini_mime (1.1.5) mini_portile2 (2.8.5) - minitest (5.22.2) + minitest (5.22.3) monetize (1.12.0) money (~> 6.12) money (6.16.0) @@ -290,10 +289,10 @@ GEM net-smtp (0.4.0) net-protocol nio4r (2.7.0) - nokogiri (1.16.2) + nokogiri (1.16.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) - pagy (7.0.8) + pagy (7.0.11) parallel (1.24.0) parser (3.3.0.5) ast (~> 2.4.1) @@ -313,7 +312,7 @@ GEM pundit (2.3.1) activesupport (>= 3.0.0) racc (1.7.3) - rack (2.2.8.1) + rack (2.2.9) rack-session (1.0.2) rack (< 3) rack-test (2.1.0) @@ -366,13 +365,13 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) - rdoc (6.6.2) + rdoc (6.6.3.1) psych (>= 4.0.0) redcarpet (3.6.0) redis-client (0.20.0) connection_pool regexp_parser (2.9.0) - reline (0.4.2) + reline (0.4.3) io-console (~> 0.5) rexml (3.2.6) rotp (6.3.0) @@ -381,23 +380,23 @@ GEM chunky_png (~> 1.0) rqrcode_core (~> 1.0) rqrcode_core (1.2.0) - rspec-core (3.12.2) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.3) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.6) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-rails (6.1.1) + rspec-support (~> 3.13.0) + rspec-rails (6.1.2) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) - rspec-core (~> 3.12) - rspec-expectations (~> 3.12) - rspec-mocks (~> 3.12) - rspec-support (~> 3.12) - rspec-support (3.12.1) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.1) rswag-api (2.13.0) activesupport (>= 3.1, < 7.2) railties (>= 3.1, < 7.2) @@ -409,7 +408,7 @@ GEM rswag-ui (2.13.0) actionpack (>= 3.1, < 7.2) railties (>= 3.1, < 7.2) - rubocop (1.60.2) + rubocop (1.62.1) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -417,24 +416,24 @@ GEM rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.30.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.30.0) - parser (>= 3.2.1.0) + rubocop-ast (1.31.2) + parser (>= 3.3.0.4) rubocop-capybara (2.20.0) rubocop (~> 1.41) - rubocop-factory_bot (2.25.0) - rubocop (~> 1.33) + rubocop-factory_bot (2.25.1) + rubocop (~> 1.41) rubocop-performance (1.20.2) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.30.0, < 2.0) - rubocop-rails (2.23.1) + rubocop-rails (2.24.0) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) - rubocop-ast (>= 1.30.0, < 2.0) - rubocop-rspec (2.26.1) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rspec (2.27.1) rubocop (~> 1.40) rubocop-capybara (~> 2.17) rubocop-factory_bot (~> 2.22) @@ -443,19 +442,19 @@ GEM ruby-progressbar (1.13.0) ruby-vips (2.1.4) ffi (~> 1.12) - ruby2_keywords (0.0.5) rubyzip (2.3.2) selenium-webdriver (4.18.1) base64 (~> 0.2) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - sentry-rails (5.16.1) + sentry-rails (5.17.1) railties (>= 5.0) - sentry-ruby (~> 5.16.1) - sentry-ruby (5.16.1) + sentry-ruby (~> 5.17.1) + sentry-ruby (5.17.1) + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) - shoulda-matchers (6.1.0) + shoulda-matchers (6.2.0) activesupport (>= 5.2.0) sidekiq (7.2.2) concurrent-ruby (< 2) @@ -476,14 +475,14 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - square.rb (36.0.0.20240222) + square.rb (36.1.0.20240320) apimatic_core (~> 0.3.0) apimatic_core_interfaces (~> 0.2.0) apimatic_faraday_client_adapter (~> 0.1.0) - standard (1.34.0) + standard (1.35.1) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) - rubocop (~> 1.60) + rubocop (~> 1.62.0) standard-custom (~> 1.0.0) standard-performance (~> 1.3) standard-custom (1.0.2) @@ -497,12 +496,12 @@ GEM stringio (3.1.0) strip_attributes (1.13.0) activemodel (>= 3.0, < 8.0) - stripe (10.11.0) - strong_migrations (1.7.0) + stripe (10.12.0) + strong_migrations (1.8.0) activerecord (>= 5.2) - thor (1.3.0) + thor (1.3.1) timeout (0.4.1) - turbo-rails (2.0.4) + turbo-rails (2.0.5) actionpack (>= 6.0.0) activejob (>= 6.0.0) railties (>= 6.0.0) @@ -540,7 +539,7 @@ PLATFORMS DEPENDENCIES active_record_extended (~> 3.2) activerecord-postgres_enum (~> 2.0) - aws-sdk-s3 (~> 1.143) + aws-sdk-s3 (~> 1.146) bcrypt (~> 3.1.20) better_errors binding_of_caller @@ -574,7 +573,7 @@ DEPENDENCIES redcarpet (~> 3.6) rotp (~> 6.3) rqrcode (~> 2.2) - rspec-rails (~> 6.1.1) + rspec-rails (~> 6.1.2) rswag-api rswag-specs rswag-ui @@ -583,18 +582,18 @@ DEPENDENCIES selenium-webdriver sentry-rails sentry-ruby - shoulda-matchers (~> 6.1) + shoulda-matchers (~> 6.2) sidekiq simplecov spring spring-watcher-listen! sprockets-rails square.rb - standard (~> 1.34) + standard (~> 1.35) stimulus-rails strip_attributes (~> 1.13) stripe - strong_migrations (~> 1.7) + strong_migrations (~> 1.8) turbo-rails tzinfo-data (~> 1.2021) view_component (~> 3.11) @@ -602,7 +601,7 @@ DEPENDENCIES webmock RUBY VERSION - ruby 3.2.3p157 + ruby 3.3.0p0 BUNDLED WITH - 2.4.12 + 2.5.6 diff --git a/app/furniture/journal/journals/_journal.html.erb b/app/furniture/journal/journals/_journal.html.erb index 2b37d4016..b9e325396 100644 --- a/app/furniture/journal/journals/_journal.html.erb +++ b/app/furniture/journal/journals/_journal.html.erb @@ -1,13 +1,12 @@ -
- <%= render Journal::NewEntryButtonComponent.new(journal: journal) %> -
- <%- @pagy, @entries = pagy(policy_scope(journal.entries.recent)) %> +<%= render Journal::NewEntryButtonComponent.new(journal: journal) %> - <%= render Journal::EntryComponent.with_collection(@entries) %> -
+
+ <%- @pagy, @entries = pagy(policy_scope(journal.entries.recent)) %> - <%== pagy_nav(@pagy) %> + <%= render Journal::EntryComponent.with_collection(@entries) %> +
- <%= render Journal::NewEntryButtonComponent.new(journal: journal) %> -
+<%== pagy_nav(@pagy) %> + +<%= render Journal::NewEntryButtonComponent.new(journal: journal) %> diff --git a/app/furniture/layouts/marketplace.html.erb b/app/furniture/layouts/marketplace.html.erb index 8dadf9bec..4e8595cc9 100644 --- a/app/furniture/layouts/marketplace.html.erb +++ b/app/furniture/layouts/marketplace.html.erb @@ -1,25 +1,24 @@ <%- content_for :content do %> -
-
- <%= turbo_frame_tag(marketplace, data: { turbo_action: :advance }) do %> - <%- if policy(marketplace).edit? %> - <%= render Marketplace::ManagementComponent.new(marketplace: marketplace) do %> - <%= yield %> - <%- end %> - <%- else %> -
-
-

- <%= t('.header', space: marketplace.space.name, room: marketplace.room.name) %> -

-
-
- <%= yield %> -
-
+
+ <%= turbo_frame_tag(marketplace, data: { turbo_action: :advance }) do %> + <%- if policy(marketplace).edit? %> + <%= render Marketplace::ManagementComponent.new(marketplace: marketplace) do %> + <%= yield %> <%- end %> + <%- else %> +
+
+

+ <%= t('.header', space: marketplace.space.name, room: marketplace.room.name) %> +

+
+
+ <%= yield %> +
+
<%- end %> <%- end %> -
+ <%- end %>
+ <%= render template: "layouts/application" %> diff --git a/app/furniture/marketplace/bazaar.rb b/app/furniture/marketplace/bazaar.rb index 2f4362604..eee8c4763 100644 --- a/app/furniture/marketplace/bazaar.rb +++ b/app/furniture/marketplace/bazaar.rb @@ -4,6 +4,7 @@ class Marketplace class Bazaar < ::Space has_many :marketplaces, through: :rooms, source: :gizmos, inverse_of: :bazaar, class_name: "Marketplace" has_many :tax_rates, inverse_of: :bazaar, dependent: :destroy + has_many :tags, inverse_of: :bazaar, dependent: :destroy def space becomes(Space) diff --git a/app/furniture/marketplace/breadcrumbs.rb b/app/furniture/marketplace/breadcrumbs.rb index c26cb14ab..b09a1e3ff 100644 --- a/app/furniture/marketplace/breadcrumbs.rb +++ b/app/furniture/marketplace/breadcrumbs.rb @@ -104,6 +104,16 @@ link t("marketplace.stripe_accounts.show.link_to"), marketplace.location(child: :stripe_account) end +crumb :marketplace_tags do |marketplace| + parent :edit_marketplace, marketplace + link(t("marketplace.tags.index.link_to"), marketplace.location(child: :tags)) +end + +crumb :new_marketplace_tag do |tag| + parent :marketplace_tags, tag.marketplace + link t("marketplace.tags.new.link_to"), marketplace.location(:new, 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) diff --git a/app/furniture/marketplace/locales/en.yml b/app/furniture/marketplace/locales/en.yml index a8c0aa18a..4da9047a0 100644 --- a/app/furniture/marketplace/locales/en.yml +++ b/app/furniture/marketplace/locales/en.yml @@ -136,3 +136,8 @@ en: payment_settings: index: link_to: "Payment Settings" + tags: + index: + link_to: "Tags" + new: + link_to: "Add Tag" diff --git a/app/furniture/marketplace/management_component.html.erb b/app/furniture/marketplace/management_component.html.erb index 5c7b32a85..76d802a9a 100644 --- a/app/furniture/marketplace/management_component.html.erb +++ b/app/furniture/marketplace/management_component.html.erb @@ -21,6 +21,7 @@ <%= link_to_child(:notification_methods, icon: :bell_alert) if policy(marketplace.notification_methods).index? %> <%= link_to_child(:flyer, icon: :qr_code) if policy(marketplace.flyer).show? %> <%= link_to_child(:vendor_representatives, icon: :building_storefront) if policy(marketplace.vendor_representatives).index? %> + <%= link_to_child(:tags, icon: :tag) if policy(marketplace.tags).index? %> <% end %> <% end %> diff --git a/app/furniture/marketplace/marketplace.rb b/app/furniture/marketplace/marketplace.rb index 0cd4a250a..1031296b2 100644 --- a/app/furniture/marketplace/marketplace.rb +++ b/app/furniture/marketplace/marketplace.rb @@ -9,6 +9,8 @@ class Marketplace < Furniture # @todo replace with through :bazaar has_many :tax_rates, inverse_of: :marketplace + has_many :tags, through: :bazaar + has_many :products, inverse_of: :marketplace, dependent: :destroy has_many :carts, inverse_of: :marketplace, dependent: :destroy has_many :orders, inverse_of: :marketplace diff --git a/app/furniture/marketplace/orders/index.html.erb b/app/furniture/marketplace/orders/index.html.erb index bfad34999..e43d72d3d 100644 --- a/app/furniture/marketplace/orders/index.html.erb +++ b/app/furniture/marketplace/orders/index.html.erb @@ -1,6 +1,5 @@ <%- breadcrumb :marketplace_orders, marketplace %> <%- @pagy, @records = pagy(orders.paid) %> -
- <%= render @records %> - <%== pagy_nav(@pagy) if @pagy.pages > 1 %> -
+ +<%= render @records %> +<%== pagy_nav(@pagy) if @pagy.pages > 1 %> diff --git a/app/furniture/marketplace/orders/show.html.erb b/app/furniture/marketplace/orders/show.html.erb index 9e0d331e1..da22fa28b 100644 --- a/app/furniture/marketplace/orders/show.html.erb +++ b/app/furniture/marketplace/orders/show.html.erb @@ -1,4 +1,3 @@ <%- breadcrumb :marketplace_order, order%> -
- <%= render order %> -
+ +<%= render order %> diff --git a/app/furniture/marketplace/policy.rb b/app/furniture/marketplace/policy.rb index bbad7025c..f0912237f 100644 --- a/app/furniture/marketplace/policy.rb +++ b/app/furniture/marketplace/policy.rb @@ -2,7 +2,7 @@ class Marketplace class Policy < ApplicationPolicy def create? return true if current_person.operator? - return true if current_person.member_of?(marketplace.space) + return true if current_person.member_of?(space) return true if shopper&.person.blank? && !current_person.authenticated? @@ -23,6 +23,7 @@ def marketplace object.marketplace if object.respond_to?(:marketplace) end + delegate :space, to: :marketplace module SpecFactories def self.included(spec) diff --git a/app/furniture/marketplace/product.rb b/app/furniture/marketplace/product.rb index 93e2dd6d2..be06f3411 100644 --- a/app/furniture/marketplace/product.rb +++ b/app/furniture/marketplace/product.rb @@ -26,6 +26,9 @@ class Product < Record has_many :ordered_products, inverse_of: :product, dependent: :destroy has_many :orders, -> { checked_out }, through: :ordered_products, inverse_of: :products + has_many :product_tags, inverse_of: :product, dependent: :destroy + has_many :tags, through: :product_tags, inverse_of: :products + has_many :product_tax_rates, inverse_of: :product, dependent: :destroy has_many :tax_rates, through: :product_tax_rates, inverse_of: :products diff --git a/app/furniture/marketplace/product/title_component.html.erb b/app/furniture/marketplace/product/title_component.html.erb index 9f21e96af..b5626e5b9 100644 --- a/app/furniture/marketplace/product/title_component.html.erb +++ b/app/furniture/marketplace/product/title_component.html.erb @@ -2,3 +2,7 @@ <%- if servings.present? %>

Serves <%= servings %>

<%- end %> + +<%- if product.tags.present? %> +

(<%= product.tags.pluck(:label).join(", ") %>)

+<%- end %> diff --git a/app/furniture/marketplace/product_policy.rb b/app/furniture/marketplace/product_policy.rb index ef63c0979..3b8d09636 100644 --- a/app/furniture/marketplace/product_policy.rb +++ b/app/furniture/marketplace/product_policy.rb @@ -4,7 +4,7 @@ class Marketplace class ProductPolicy < Policy alias_method :product, :object def permitted_attributes(_params = nil) - %i[name description price_cents price_currency price photo restore servings] + [tax_rate_ids: []] + %i[name description price_cents price_currency price photo restore servings] + [tag_ids: [], tax_rate_ids: []] end def update? diff --git a/app/furniture/marketplace/product_tag.rb b/app/furniture/marketplace/product_tag.rb new file mode 100644 index 000000000..368ddacd3 --- /dev/null +++ b/app/furniture/marketplace/product_tag.rb @@ -0,0 +1,8 @@ +class Marketplace + class ProductTag < Record + self.table_name = :marketplace_product_tags + + belongs_to :product, inverse_of: :product_tags + belongs_to :tag, inverse_of: :product_tags + end +end diff --git a/app/furniture/marketplace/products/_form.html.erb b/app/furniture/marketplace/products/_form.html.erb index c4e3f705b..4a38ab9db 100644 --- a/app/furniture/marketplace/products/_form.html.erb +++ b/app/furniture/marketplace/products/_form.html.erb @@ -5,6 +5,8 @@ <%= render "number_field", { attribute: :servings, form: f, min: 0, step: 1} %> <%= render "money_field", { attribute: :price, form: f, min: 0, step: 0.01} %> + <%= render "collection_check_boxes", { attribute: :tag_ids, collection: bazaar.tags, value_method: :id, + text_method: :label, form: f} %> <%= render "collection_check_boxes", { attribute: :tax_rate_ids, collection: bazaar.tax_rates, value_method: :id, text_method: :label, form: f} %> <%- if product.photo.present? %>
diff --git a/app/furniture/marketplace/routes.rb b/app/furniture/marketplace/routes.rb index d8bb9061d..52ae8e6e0 100644 --- a/app/furniture/marketplace/routes.rb +++ b/app/furniture/marketplace/routes.rb @@ -16,6 +16,7 @@ def self.append_routes(router) router.resources :products router.resource :stripe_account, only: [:show, :new, :create] router.resources :stripe_events + router.resources :tags router.resources :tax_rates router.resources :vendor_representatives router.resources :payment_settings, only: [:index] diff --git a/app/furniture/marketplace/tag.rb b/app/furniture/marketplace/tag.rb new file mode 100644 index 000000000..9d8e98db2 --- /dev/null +++ b/app/furniture/marketplace/tag.rb @@ -0,0 +1,14 @@ +class Marketplace + class Tag < Record + self.table_name = "marketplace_tags" + + belongs_to :bazaar, inverse_of: :tags + has_many :product_tags, inverse_of: :tag, dependent: :destroy + has_many :products, through: :product_tags, inverse_of: :tags + + validates :label, uniqueness: {case_sensitive: false, scope: :bazaar_id} + + attr_accessor :marketplace + location(parent: :marketplace) + end +end diff --git a/app/furniture/marketplace/tag_policy.rb b/app/furniture/marketplace/tag_policy.rb new file mode 100644 index 000000000..cd43c1be4 --- /dev/null +++ b/app/furniture/marketplace/tag_policy.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class Marketplace + class TagPolicy < Policy + alias_method :tag, :object + def space + tag.bazaar + end + + def permitted_attributes(_params = nil) + %i[label] + end + + def update? + return false unless current_person.authenticated? + + super + end + + alias_method :create?, :update? + + def show? + true + end + + class Scope < ApplicationScope + def resolve + scope.all + end + end + end +end diff --git a/app/furniture/marketplace/tags/_form.html.erb b/app/furniture/marketplace/tags/_form.html.erb new file mode 100644 index 000000000..acd3ab69e --- /dev/null +++ b/app/furniture/marketplace/tags/_form.html.erb @@ -0,0 +1,6 @@ +<%= form_with(model: tag.location) do |form| %> + +<%= render "text_field", attribute: :label, form: form %> + +<%= form.submit %> +<%- end %> diff --git a/app/furniture/marketplace/tags/index.html.erb b/app/furniture/marketplace/tags/index.html.erb new file mode 100644 index 000000000..61832ff4c --- /dev/null +++ b/app/furniture/marketplace/tags/index.html.erb @@ -0,0 +1,20 @@ +<%- breadcrumb :marketplace_tags, marketplace %> + +<%= render CardComponent.new do |card| %> + + <%- marketplace.tags.each do |tag| %> + <%- tag.marketplace = marketplace %> +
+ + <%= tag.label %> + +
+ <%- 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 %> +<%- end %> diff --git a/app/furniture/marketplace/tags/new.html.erb b/app/furniture/marketplace/tags/new.html.erb new file mode 100644 index 000000000..d5e9383ef --- /dev/null +++ b/app/furniture/marketplace/tags/new.html.erb @@ -0,0 +1,2 @@ +<%- breadcrumb :new_marketplace_tag, mtag %> +<%= render "form", tag: mtag %> diff --git a/app/furniture/marketplace/tags_controller.rb b/app/furniture/marketplace/tags_controller.rb new file mode 100644 index 000000000..8e25ffb8a --- /dev/null +++ b/app/furniture/marketplace/tags_controller.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class Marketplace + class TagsController < Controller + # Apparently, `tag` is used inside of `turbo_frame_tag`, so if we define a + # helper_method named `tag` our method gets called when it really shouldn't + # be... so `mtag` it is. For now. + expose :mtag, scope: -> { tags }, model: Tag + expose :tags, -> { policy_scope(bazaar.tags.create_with(marketplace: marketplace)) } + + def new + authorize(mtag) + end + + def create + if authorize(mtag).save + redirect_to marketplace.location(child: :tags) + else + render :new + end + end + + def index + skip_authorization + end + + def mtag_params + policy(Tag).permit(params.require(:tag)) + end + end +end diff --git a/app/furniture/section_navigation/section_navigations/_section_navigation.html.erb b/app/furniture/section_navigation/section_navigations/_section_navigation.html.erb index e737fceec..a2b41e182 100644 --- a/app/furniture/section_navigation/section_navigations/_section_navigation.html.erb +++ b/app/furniture/section_navigation/section_navigations/_section_navigation.html.erb @@ -1,13 +1,13 @@
<% policy_scope(section_navigation.rooms).each do |room| %> <%= link_to polymorphic_path(room.location), id: dom_id(room, :link_to), class: "group no-underline" do %> - <%= render CardComponent.new(media: room.hero_image, classes: "flex flex-col justify-between") do |card| %> + <%= render CardComponent.new(media: room.hero_image, classes: "flex flex-col justify-between h-full") do |card| %>

<%= room.description %>

-