Compare commits

..

1 Commits

Author SHA1 Message Date
Ahmed Ejaz
7cab04553a add test data 2025-09-18 00:51:31 +05:00
1247 changed files with 56439 additions and 43655 deletions

2
.browserslistrc Normal file
View File

@@ -0,0 +1,2 @@
defaults
IE 11

56
.codeclimate.yml Normal file
View File

@@ -0,0 +1,56 @@
version: "2"
plugins:
rubocop:
enabled: true
channel: "rubocop-1-12"
config:
file: ".rubocop.yml"
scss-lint:
enabled: true
checks:
ImportantRule:
enabled: false
VendorPrefix:
enabled: false
LeadingZero:
enabled: false
PropertySortOrder:
enabled: false
StringQuotes:
enabled: false
DeclarationOrder:
enabled: false
NestingDepth:
enabled: false
duplication:
enabled: true
exclude_patterns:
- "db/**"
- "config/initializers/active_record_postgresql_referential_integrity_patch.rb"
checks:
argument-count:
enabled: false
complex-logic:
enabled: false
file-lines:
enabled: false
method-complexity:
enabled: false
method-count:
enabled: false
method-lines:
enabled: false
nested-control-flow:
enabled: false
return-statements:
enabled: false
similar-code:
enabled: false
identical-code:
enabled: false
exclude_patterns:
- "spec/**/*"
- "vendor/**/*"
- "app/assets/javascripts/shared/*"
- "app/assets/javascripts/jquery-migrate-1.0.0.js"

View File

@@ -20,6 +20,7 @@ STRIPE_INSTANCE_SECRET_KEY="bogus_key"
STRIPE_CUSTOMER="bogus_customer"
STRIPE_ACCOUNT="bogus_account"
STRIPE_CLIENT_ID="bogus_client_id"
STRIPE_PUBLIC_TEST_API_KEY="bogus_stripe_publishable_key"
SITE_URL="test.host"

1
.gitattributes vendored
View File

@@ -8,3 +8,4 @@
# Same thing for following files, but they don't have an sh extension
pre-commit eol=lf
webpack-dev-server eol=lf
install-bundler eol=lf

View File

@@ -1,4 +1,4 @@
## What? Why?
#### What? Why?
- Closes # <!-- Insert issue number here. -->
@@ -7,7 +7,7 @@
## What should we test?
#### What should we test?
<!-- List which features should be tested and how.
This can be similar to the Steps to Reproduce in the issue.
Also think of other parts of the app which could be affected
@@ -16,7 +16,7 @@
- Visit ... page.
-
## Release notes
#### Release notes
<!-- Please select one for your PR and delete the other. -->
@@ -33,12 +33,12 @@ Changelog Category (reviewers may add a label for the release notes):
The title of the pull request will be included in the release notes.
## Dependencies
#### Dependencies
<!-- Does this PR depend on another one?
Add the link or remove this section. -->
## Documentation updates
#### Documentation updates
<!-- Are there any wiki pages that need updating after merging this PR?
List them here or remove this section. -->

View File

@@ -4,35 +4,12 @@
# Most of the configuration here is not used for security updates though.
version: 2
multi-ecosystem-groups:
turbo_power:
schedule:
interval: "daily"
updates:
- package-ecosystem: "bundler"
directory: "/"
patterns: ["turbo_power"]
multi-ecosystem-group: "turbo_power"
# Only specific requirements are specified in Gemfile, so don't touch it.
versioning-strategy: lockfile-only
- package-ecosystem: "npm"
directory: "/"
patterns: ["turbo_power"]
multi-ecosystem-group: "turbo_power"
# Only specific requirements are specified in package.json, so don't touch it.
versioning-strategy: lockfile-only
- package-ecosystem: "bundler"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 7
# Only specific requirements are specified in Gemfile, so don't touch it.
versioning-strategy: lockfile-only
@@ -41,8 +18,6 @@ updates:
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 7
# Only specific requirements are specified in package.json, so don't touch it.
versioning-strategy: lockfile-only
# All versions are specified in package.json, so please update them.
versioning-strategy: increase

13
.github/pull_request.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"pull_request": {
"number": 13490,
"title": "Bump rails from 7.0.4 to 7.0.8",
"id": 99999999,
"user": { "login": "dependabot[bot]" }
},
"repository": {
"owner": { "login": "openfoodnetwork" },
"name": "openfoodnetwork"
},
"sender": { "login": "dependabot[bot]" }
}

View File

@@ -1,15 +0,0 @@
{
"pull_request": {
"number": 13545,
"title": "Bump test from 7.0.4 to 7.0.8",
"user": {
"login": "dependabot[bot]"
}
},
"repository": {
"owner": {
"login": "openfoodfoundation"
},
"name": "openfoodnetwork"
}
}

View File

@@ -46,7 +46,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.8.1
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
@@ -124,7 +124,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.8.1
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
@@ -211,7 +211,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.8.1
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
@@ -290,7 +290,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.8.1
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6

View File

@@ -1,10 +1,10 @@
name: Linters
on: [pull_request]
on: [push, pull_request]
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
lint:
name: reviewdog
name: prettier and rubocop
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -21,10 +21,21 @@ jobs:
- run: git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
- uses: reviewdog/action-setup@v1
- name: prettier
uses: EPMatt/reviewdog-action-prettier@v1
with:
reviewdog_version: v0.21.0
github_token: ${{ secrets.github_token }}
reporter: github-pr-check
level: error
fail_on_error: true
- run: ./script/reviewdog.sh
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.github_token }}
- name: rubocop
uses: reviewdog/action-rubocop@v2
with:
rubocop_version: gemfile
rubocop_extensions: rubocop-rails:gemfile rubocop-rspec:gemfile
reporter: github-pr-check
level: error
filter_mode: nofilter
use_bundler: true
fail_level: any

View File

@@ -1,10 +1,7 @@
name: Auto-move Dependabot PRs to Code Review
permissions:
contents: read
pull-requests: read
on:
pull_request_target:
pull_request:
types: [opened]
jobs:
@@ -12,24 +9,15 @@ jobs:
runs-on: ubuntu-latest
if: github.event.pull_request.user.login == 'dependabot[bot]' || startsWith(github.event.pull_request.title, 'Bump')
steps:
- name: Generate GitHub App Token
id: app-token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.DEPENDABOT_PR_APP_ID }}
private_key: ${{ secrets.DEPENDABOT_PR_APP_PRIVATE_KEY }}
installation_retrieval_mode: id
installation_retrieval_payload: ${{ secrets.DEPENDABOT_PR_APP_INSTALLATION_ID }}
- name: Move PR to Code Review in Project v2
uses: actions/github-script@v7
with:
github-token: ${{ steps.app-token.outputs.token }}
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const projectNumber = 8; // for "OFN Delivery board"
const org = "openfoodfoundation";
const repo = context.repo.repo;
const prNumber = context.payload.pull_request.number;
const org = context.repo.owner;
const repo = context.repo.repo;
const prNumber = context.payload.pull_request.number;
const statusFieldName = "Status";
const statusValue = "Code review 🔎";

1
.gitignore vendored
View File

@@ -59,4 +59,3 @@ yarn-debug.log*
/config/credentials.yml.enc
/config/master.key
.secrets

View File

@@ -2,12 +2,19 @@
# frameworks such as Jekyll/Middleman
skip_frontmatter: false
inherits_from: .haml-lint_todo.yml
linters:
AltText:
enabled: false
ClassAttributeWithStaticValue:
enabled: true
ClassesBeforeIds:
enabled: true
ConsecutiveComments:
enabled: true
ConsecutiveSilentScripts:
enabled: true
max_consecutive: 2
@@ -25,6 +32,7 @@ linters:
enabled: true
LineLength:
enabled: true
max: 80
MultilinePipe:
@@ -39,11 +47,24 @@ linters:
RuboCop:
enabled: false
RubyComments:
enabled: true
SpaceBeforeScript:
enabled: true
SpaceInsideHashAttributes:
enabled: true
style: no_space
TagName:
enabled: true
TrailingWhitespace:
enabled: true
UnnecessaryInterpolation:
enabled: true
UnnecessaryStringOutput:
enabled: true

View File

@@ -1,153 +0,0 @@
# This configuration was generated by
# `haml-lint --auto-gen-config`
# on 2025-10-30 09:19:50 +0100 using Haml-Lint version 0.66.0.
# The point is for the user to remove these configuration records
# one by one as the lints are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of Haml-Lint, may require this file to be generated again.
linters:
# Offense count: 35
ClassAttributeWithStaticValue:
enabled: false
# Offense count: 77
ClassesBeforeIds:
enabled: false
# Offense count: 18
ConsecutiveComments:
enabled: false
# Offense count: 22
ConsecutiveSilentScripts:
exclude:
- "app/views/admin/contents/_fieldset.html.haml"
- "app/views/admin/enterprises/form/_tag_rules.html.haml"
- "app/views/admin/order_cycles/edit.html.haml"
- "app/views/admin/products_v3/product_preview.turbo_stream.haml"
- "app/views/admin/reports/_date_range_form.html.haml"
- "app/views/checkout/_details.html.haml"
- "app/views/checkout/_payment.html.haml"
- "app/views/spree/admin/adjustments/_adjustments_table.html.haml"
- "app/views/spree/admin/orders/customer_details/_address_form.html.haml"
- "app/views/spree/admin/tax_categories/index.html.haml"
- "app/views/spree/admin/users/index.html.haml"
# Offense count: 14
FinalNewline:
exclude:
- "app/assets/javascripts/templates/shared/question_mark_with_tooltip.html.haml"
- "app/views/admin/enterprises/form/_social.html.haml"
- "app/views/admin/json/_injection_ams.html.haml"
- "app/views/admin/order_cycles/_date_time_warning_modal_content.html.haml"
- "app/views/admin/order_cycles/edit.html.haml"
- "app/views/admin/product_import/_ams_data.html.haml"
- "app/views/admin/reports/_row_group.haml"
- "app/views/admin/reports/filters/_enterprise_fee_summary.html.haml"
- "app/views/admin/reports/filters/_users_and_enterprises.html.haml"
- "app/views/shop/_blocked_cookies.html.haml"
- "app/views/spree/admin/orders/_invoice/_order_note.html.haml"
- "app/views/spree/admin/orders/invoice4.html.haml"
- "app/views/spree/admin/taxons/destroy_taxon.turbo_stream.haml"
- "app/views/spree/admin/users/_email_confirmation.html.haml"
# Offense count: 130
IdNames:
enabled: false
# Offense count: 5
Indentation:
exclude:
- "app/views/admin/products_v3/clone.turbo_stream.haml"
- "app/views/admin/products_v3/destroy_product_variant.turbo_stream.haml"
- "app/views/spree/admin/taxons/destroy_taxon.turbo_stream.haml"
# Offense count: 191
InlineStyles:
enabled: false
# Offense count: 589
InstanceVariables:
enabled: false
# Offense count: 2
LeadingCommentSpace:
exclude:
- "app/views/admin/reports/_row_group.haml"
# Offense count: 2331
LineLength:
enabled: false
# Offense count: 1
MultilinePipe:
exclude:
- "app/views/admin/reports/_rendering_options.html.haml"
# Offense count: 2
MultilineScript:
exclude:
- "app/views/admin/products_v3/product_preview.turbo_stream.haml"
- "app/views/checkout/_voucher_section.html.haml"
# Offense count: 2
RepeatedId:
exclude:
- "app/assets/javascripts/templates/admin/save_bar.html.haml"
# Offense count: 24
RubyComments:
enabled: false
# Offense count: 104
SpaceBeforeScript:
enabled: false
# Offense count: 3345
SpaceInsideHashAttributes:
enabled: false
# Offense count: 22
TrailingEmptyLines:
enabled: false
# Offense count: 73
TrailingWhitespace:
enabled: false
# Offense count: 13
UnnecessaryInterpolation:
exclude:
- "app/components/example_component/example_component.html.haml"
- "app/views/admin/product_import/_entries_table.html.haml"
- "app/views/admin/product_import/import.html.haml"
- "app/views/admin/variant_overrides/_filters.html.haml"
- "app/views/registration/steps/_introduction.html.haml"
- "app/views/spree/order_mailer/_shipping.html.haml"
- "app/views/spree/order_mailer/invoice_email.html.haml"
- "app/views/spree/shared/_shipment_delivery_details.html.haml"
- "app/views/spree/shared/_shipment_pickup_details.html.haml"
# Offense count: 68
UnnecessaryStringOutput:
enabled: false
# Offense count: 14
ViewLength:
exclude:
- "app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml"
- "app/views/admin/customers/index.html.haml"
- "app/views/admin/enterprises/_new_form.html.haml"
- "app/views/admin/enterprises/form/_shop_preferences.html.haml"
- "app/views/admin/product_import/_import_review.html.haml"
- "app/views/admin/products_v3/product_preview.turbo_stream.haml"
- "app/views/checkout/_details.html.haml"
- "app/views/groups/show.html.haml"
- "app/views/producer_mailer/order_cycle_report.html.haml"
- "app/views/shared/_footer.html.haml"
- "app/views/spree/admin/orders/bulk_management.html.haml"
- "app/views/spree/admin/orders/invoice4.html.haml"
- "app/views/spree/admin/products/new.html.haml"
- "app/views/spree/admin/variants/_form.html.haml"

6
.hound.yml Normal file
View File

@@ -0,0 +1,6 @@
rubocop:
config_file: .rubocop_styleguide.yml
scss:
config_file: .scss-lint.yml
haml:
config_file: .haml-lint.yml

View File

@@ -1 +1 @@
24.10.0
17.9.1

2
.rspec
View File

@@ -1 +1 @@
--require spec_helper
--require base_spec_helper

View File

@@ -4,7 +4,7 @@
#
# The configuration is split into three files. Look into those files for more details.
#
plugins:
require:
- rubocop-capybara
- rubocop-factory_bot
- rubocop-rails

View File

@@ -94,7 +94,7 @@ Metrics/PerceivedComplexity:
Enabled: true
Max: 14 # default 8
Naming/PredicatePrefix:
Naming/PredicateName:
Enabled: false
Naming/VariableNumber:

View File

@@ -1,44 +1,11 @@
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400 --no-auto-gen-timestamp`
# using RuboCop version 1.81.7.
# using RuboCop version 1.64.1.
# 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
# versions of RuboCop, may require this file to be generated again.
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: RequireParenthesesForMethodChains.
Lint/AmbiguousRange:
Exclude:
- 'app/models/concerns/permalink_generator.rb'
# Offense count: 6
Lint/CopDirectiveSyntax:
Exclude:
- 'app/services/orders/bulk_cancel_service.rb'
- 'lib/tasks/simplecov.rake'
- 'spec/models/database_spec.rb'
- 'spec/system/admin/bulk_order_management_spec.rb'
- 'spec/system/admin/enterprise_relationships_spec.rb'
# Offense count: 2
Lint/DuplicateMethods:
Exclude:
- 'app/models/spree/order.rb'
- 'engines/order_management/app/services/order_management/subscriptions/form.rb'
# Offense count: 3
Lint/UselessConstantScoping:
Exclude:
- 'app/services/weights_and_measures.rb'
- 'lib/reporting/report_metadata_builder.rb'
# Offense count: 1
Lint/UselessOr:
Exclude:
- 'app/models/product_import/entry_validator.rb'
# Offense count: 24
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
Metrics/AbcSize:
@@ -75,12 +42,12 @@ Metrics/BlockLength:
- 'lib/tasks/data.rake'
# Offense count: 1
# Configuration parameters: CountBlocks, CountModifierForms, Max.
# Configuration parameters: CountBlocks, Max.
Metrics/BlockNesting:
Exclude:
- 'app/models/spree/payment/processing.rb'
# Offense count: 49
# Offense count: 47
# Configuration parameters: CountComments, Max, CountAsOne.
Metrics/ClassLength:
Exclude:
@@ -121,8 +88,6 @@ Metrics/ClassLength:
- 'app/services/cart_service.rb'
- 'app/services/order_cycles/form_service.rb'
- 'app/services/orders/sync_service.rb'
- 'app/services/permissions/order.rb'
- 'app/services/products_renderer.rb'
- 'app/services/sets/product_set.rb'
- 'engines/order_management/app/services/order_management/order/updater.rb'
- 'lib/open_food_network/enterprise_fee_calculator.rb'
@@ -133,8 +98,9 @@ Metrics/ClassLength:
- 'lib/reporting/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_producer.rb'
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
- 'app/services/permissions/order.rb'
# Offense count: 37
# Offense count: 30
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
Metrics/CyclomaticComplexity:
Exclude:
@@ -157,24 +123,20 @@ Metrics/CyclomaticComplexity:
- 'app/models/spree/tax_rate.rb'
- 'app/models/spree/zone.rb'
- 'lib/open_food_network/enterprise_issue_validator.rb'
- 'lib/reporting/reports/orders_and_fulfillment/order_cycle_customer_totals.rb'
- 'lib/reporting/reports/orders_and_fulfillment/order_cycle_supplier_totals.rb'
- 'lib/reporting/reports/payments/itemised_payment_totals.rb'
- 'lib/reporting/reports/payments/payment_totals.rb'
- 'lib/reporting/reports/sales_tax/sales_tax_totals_by_producer.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
- 'lib/spree/core/controller_helpers/order.rb'
- 'lib/spree/core/controller_helpers/respond_with.rb'
- 'lib/spree/localized_number.rb'
- 'spec/models/product_importer_spec.rb'
# Offense count: 22
# Offense count: 23
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength:
Exclude:
- 'app/controllers/admin/enterprises_controller.rb'
- 'app/controllers/payment_gateways/paypal_controller.rb'
- 'app/controllers/spree/orders_controller.rb'
- 'app/helpers/spree/admin/navigation_helper.rb'
- 'app/models/spree/ability.rb'
- 'app/models/spree/gateway/pay_pal_express.rb'
- 'app/models/spree/order/checkout.rb'
@@ -187,7 +149,7 @@ Metrics/MethodLength:
- 'lib/spree/localized_number.rb'
- 'lib/tasks/sample_data/product_factory.rb'
# Offense count: 10
# Offense count: 47
# Configuration parameters: CountComments, Max, CountAsOne.
Metrics/ModuleLength:
Exclude:
@@ -212,7 +174,7 @@ Metrics/ParameterLists:
- 'spec/support/controller_requests_helper.rb'
- 'spec/system/admin/reports_spec.rb'
# Offense count: 4
# Offense count: 3
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
Metrics/PerceivedComplexity:
Exclude:
@@ -220,165 +182,16 @@ Metrics/PerceivedComplexity:
- 'app/models/spree/ability.rb'
- 'app/models/spree/order/checkout.rb'
# Offense count: 1
# Configuration parameters: EnforcedStyle, AllowedPatterns, ForbiddenIdentifiers, ForbiddenPatterns.
# SupportedStyles: snake_case, camelCase
# ForbiddenIdentifiers: __id__, __send__
Naming/MethodName:
Exclude:
- 'engines/dfc_provider/lib/dfc_provider/catalog_item.rb'
# Offense count: 1
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
Naming/MethodParameterName:
Exclude:
- 'engines/dfc_provider/lib/dfc_provider/catalog_item.rb'
# Offense count: 60
# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
# AllowedMethods: call
# WaywardPredicates: nonzero?
Naming/PredicateMethod:
Exclude:
- 'app/controllers/admin/product_import_controller.rb'
- 'app/controllers/api/v0/order_cycles_controller.rb'
- 'app/controllers/spree/admin/overview_controller.rb'
- 'app/controllers/spree/admin/payments_controller.rb'
- 'app/controllers/voucher_adjustments_controller.rb'
- 'app/forms/enterprise_fees_bulk_update.rb'
- 'app/forms/schedule_form.rb'
- 'app/helpers/spree/orders_helper.rb'
- 'app/models/concerns/variant_stock.rb'
- 'app/models/enterprise.rb'
- 'app/models/enterprise_fee.rb'
- 'app/models/invoice/data_presenter.rb'
- 'app/models/order_cycle.rb'
- 'app/models/product_import/entry_processor.rb'
- 'app/models/product_import/entry_validator.rb'
- 'app/models/spree/order.rb'
- 'app/models/spree/order_contents.rb'
- 'app/models/spree/payment.rb'
- 'app/models/spree/payment/processing.rb'
- 'app/models/spree/state_change.rb'
- 'app/models/spree/user.rb'
- 'app/reflexes/admin/orders_reflex.rb'
- 'app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb'
- 'app/serializers/api/admin/index_enterprise_serializer.rb'
- 'app/serializers/api/admin/index_order_cycle_serializer.rb'
- 'app/serializers/api/admin/order_cycle_serializer.rb'
- 'app/serializers/api/admin/order_serializer.rb'
- 'app/serializers/api/admin/schedule_serializer.rb'
- 'app/serializers/api/admin/subscription_line_item_serializer.rb'
- 'app/serializers/api/admin/user_serializer.rb'
- 'app/serializers/api/admin/variant_serializer.rb'
- 'app/serializers/api/enterprise_shopfront_serializer.rb'
- 'app/serializers/api/enterprise_thin_serializer.rb'
- 'app/serializers/api/order_serializer.rb'
- 'app/serializers/api/uncached_enterprise_serializer.rb'
- 'app/services/cart_service.rb'
- 'app/services/orders/fetch_adjustments_service.rb'
- 'app/services/orders/workflow_service.rb'
- 'app/services/sets/model_set.rb'
- 'app/services/sets/order_cycle_set.rb'
- 'app/services/sets/product_set.rb'
- 'engines/dfc_provider/app/controllers/dfc_provider/addresses_controller.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/reporting/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_order.rb'
- 'lib/reporting/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_producer.rb'
- 'lib/tasks/data/check_invalid_address_used.rake'
# Offense count: 3
# Configuration parameters: EnforcedStyle, AllowedIdentifiers, AllowedPatterns, ForbiddenIdentifiers, ForbiddenPatterns.
# SupportedStyles: snake_case, camelCase
Naming/VariableName:
Exclude:
- 'engines/dfc_provider/lib/dfc_provider/catalog_item.rb'
# Offense count: 6
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/FindByOrAssignmentMemoization:
Exclude:
- 'app/controllers/admin/customers_controller.rb'
- 'app/controllers/admin/resource_controller.rb'
- 'app/controllers/api/v0/enterprise_fees_controller.rb'
- 'app/controllers/api/v0/order_cycles_controller.rb'
- 'lib/stripe/account_connector.rb'
# Offense count: 32
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/OrderArguments:
Exclude:
- 'app/controllers/admin/enterprise_fees_controller.rb'
- 'app/controllers/admin/enterprises_controller.rb'
- 'app/controllers/admin/order_cycles_controller.rb'
- 'app/controllers/admin/product_import_controller.rb'
- 'app/controllers/api/v0/states_controller.rb'
- 'app/controllers/spree/admin/overview_controller.rb'
- 'app/controllers/spree/admin/products_controller.rb'
- 'app/helpers/enterprises_helper.rb'
- 'app/models/enterprise.rb'
- 'app/models/enterprise_group.rb'
- 'app/models/enterprise_relationship_permission.rb'
- 'app/models/order_cycle.rb'
- 'app/models/product_import/product_importer.rb'
- 'app/models/schedule.rb'
- 'app/models/spree/country.rb'
- 'app/models/spree/order.rb'
- 'app/models/spree/shipping_rate.rb'
- 'app/models/spree/user.rb'
- 'app/models/spree/zone.rb'
- 'app/models/subscription_line_item.rb'
- 'app/models/tag_rule.rb'
- 'app/models/variant_override.rb'
- 'lib/open_food_network/address_finder.rb'
- 'spec/services/orders/generate_invoice_service_spec.rb'
- 'spec/system/admin/order_cycles/simple_spec.rb'
# Offense count: 3
# This cop supports safe autocorrection (--autocorrect).
Rails/Presence:
Exclude:
- 'app/controllers/admin/enterprises_controller.rb'
- 'app/models/spree/product.rb'
# Offense count: 6
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Severity.
Rails/RedirectBackOrTo:
Exclude:
- 'app/controllers/admin/order_cycles_controller.rb'
- 'app/controllers/locales_controller.rb'
- 'app/controllers/spree/admin/invoices_controller.rb'
- 'app/controllers/spree/admin/orders_controller.rb'
- 'app/controllers/spree/admin/return_authorizations_controller.rb'
# Offense count: 1
# Configuration parameters: TransactionMethods.
Rails/TransactionExitStatement:
Exclude:
- 'app/services/place_proxy_order.rb'
# Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/ArrayIntersect:
Exclude:
- 'app/models/spree/ability.rb'
- 'app/models/spree/variant.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/BitwisePredicate:
Exclude:
- 'app/helpers/admin/enterprises_helper.rb'
# Offense count: 23
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle, EnforcedStyleForClasses, EnforcedStyleForModules.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: nested, compact
# SupportedStylesForClasses: ~, nested, compact
# SupportedStylesForModules: ~, nested, compact
Style/ClassAndModuleChildren:
Exclude:
- 'app/models/calculator/flat_percent_per_item.rb'
@@ -404,33 +217,13 @@ Style/ClassAndModuleChildren:
- 'lib/open_food_network/locking.rb'
- 'spec/models/spree/payment_method_spec.rb'
# Offense count: 14
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/CollectionQuerying:
Exclude:
- 'app/controllers/spree/credit_cards_controller.rb'
- 'app/models/product_import/product_importer.rb'
- 'app/models/product_import/spreadsheet_entry.rb'
- 'app/models/spree/order.rb'
- 'app/models/spree/order_inventory.rb'
- 'app/models/spree/payment_method.rb'
- 'app/models/spree/user.rb'
- 'app/models/stripe_account.rb'
- 'app/services/order_cycles/warning_service.rb'
- 'lib/reporting/report_renderer.rb'
- 'lib/tasks/sample_data.rake'
# Offense count: 2
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/HashSlice:
Exclude:
- 'app/services/product_filters.rb'
- 'lib/reporting/report_row_builder.rb'
# Offense count: 1
# Offense count: 4
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/MapToHash:
Exclude:
- 'lib/reporting/report_query_template.rb'
- 'lib/reporting/report_row_builder.rb'
- 'lib/reporting/reports/enterprise_fee_summary/fee_summary.rb'
- 'lib/tasks/sample_data/user_factory.rb'
# Offense count: 38
@@ -461,22 +254,3 @@ Style/OptionalBooleanParameter:
- 'engines/order_management/app/services/order_management/stock/estimator.rb'
- 'lib/spree/core/controller_helpers/order.rb'
- 'spec/support/request/web_helper.rb'
# Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/RedundantFormat:
Exclude:
- 'spec/models/product_importer_spec.rb'
- 'spec/requests/checkout/stripe_sca_spec.rb'
- 'spec/system/consumer/account/cards_spec.rb'
# Offense count: 8
# Configuration parameters: Max.
Style/SafeNavigationChainLength:
Exclude:
- 'app/controllers/concerns/extra_fields.rb'
- 'app/services/customer_syncer.rb'
- 'app/services/fdc_offer_broker.rb'
- 'engines/dfc_provider/app/services/dfc_catalog.rb'
- 'engines/dfc_provider/app/services/image_builder.rb'
- 'engines/dfc_provider/app/services/quantitative_value_builder.rb'

View File

@@ -1 +1 @@
3.4.8
3.1.4

19
.scss-lint.yml Normal file
View File

@@ -0,0 +1,19 @@
scss_files: 'app/assets/stylesheets/**/*.css.scss'
exclude: 'app/assets/stylesheets/shared/**'
linters:
ImportantRule:
enabled: false
VendorPrefix:
enabled: false
LeadingZero:
enabled: false
PropertySortOrder:
enabled: false
StringQuotes:
enabled: false
DeclarationOrder:
enabled: false
NestingDepth:
enabled: false

View File

@@ -1,4 +0,0 @@
# .secrets file define github secrets value locally
DEPENDABOT_PR_APP_ID=123456
DEPENDABOT_PR_APP_INSTALLATION_ID=123456
DEPENDABOT_PR_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n....\n-----END RSA PRIVATE KEY-----"

View File

@@ -1,4 +1,4 @@
FROM ruby:3.4.8-alpine3.19 AS base
FROM ruby:3.1.4-alpine3.19 AS base
ENV LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
TZ=Europe/London \
@@ -31,4 +31,4 @@ FROM development-base
COPY . $RAILS_ROOT
COPY Gemfile Gemfile.lock ./
RUN bundle install --jobs "$(nproc)"
COPY --from=yarn-dependencies $RAILS_ROOT/node_modules ./node_modules
COPY --from=yarn-dependencies $RAILS_ROOT/node_modules ./node_modules

View File

@@ -83,8 +83,11 @@ RUN wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.z
# Copy code and install app dependencies
COPY . /usr/src/app/
# Install Bundler
RUN ./script/install-bundler
# Install front-end dependencies
RUN yarn install
# Run bundler install in parallel with the amount of available CPUs
RUN bundle install --jobs="$(nproc)"
RUN bundle install --jobs="$(nproc)"

View File

@@ -73,7 +73,7 @@ To login as the default user, use:
email: ofn@example.com
password: ofn123
See [Locale and sample data] about loading data.
Seee [Locale and sample data] about loading data.
### Testing

32
Gemfile
View File

@@ -1,6 +1,6 @@
# frozen_string_literal: true
source 'https://gem.coop'
source 'https://rubygems.org'
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
ruby File.read('.ruby-version').chomp
@@ -14,19 +14,16 @@ gem "active_storage_validations"
gem "aws-sdk-s3", require: false
gem "image_processing"
gem 'activemerchant'
gem 'angular-rails-templates'
gem 'activemerchant', '>= 1.78.0'
gem 'angular-rails-templates', '>= 0.3.0'
gem 'ransack', '~> 4.1.0'
gem 'responders'
gem 'shakapacker', '8.4.0'
# Indirect dependency but we access it directly in JS specs.
# It turns out to be hard to upgrade but please do if you can.
gem 'sprockets', '~> 3.7'
gem 'webpacker', '~> 5'
gem 'i18n'
gem 'i18n-js', '~> 3.9.0'
gem 'rails-i18n'
gem 'rails_safe_tasks', '~> 1.0'
gem "activerecord-import"
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-7"
@@ -43,13 +40,13 @@ gem 'web', path: './engines/web'
gem "activerecord-postgresql-adapter"
gem "arel-helpers", "~> 2.12"
gem "pg"
gem "pg", "~> 1.2.3"
gem 'acts_as_list', '1.0.4'
gem 'cancancan', '~> 1.15.0'
gem 'digest'
gem 'ffaker'
gem 'highline'
gem 'highline', '2.0.3' # Necessary for the install generator
gem 'json'
gem 'monetize', '~> 1.11'
gem 'paranoia', '~> 2.4'
@@ -57,8 +54,7 @@ gem 'state_machines-activerecord'
gem 'stringex', '~> 2.8.5', require: false
gem 'paypal-sdk-merchant', '1.117.2'
gem 'stripe', '~> 15'
gem "taler"
gem 'stripe'
gem 'devise'
gem 'devise-encryptable'
@@ -112,7 +108,7 @@ gem "turbo_power"
gem "turbo-rails"
gem 'combine_pdf'
gem 'wicked_pdf', github: "openfoodfoundation/wicked_pdf", branch: "master"
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
gem 'immigrant'
@@ -127,8 +123,7 @@ gem 'angular_rails_csrf'
gem 'jquery-rails', '4.4.0'
gem 'jquery-ui-rails', '~> 4.2'
gem "select2-rails", github: "openfoodfoundation/select2-rails",
branch: "v349_with-relaxed-dependencies"
gem "select2-rails", github: "openfoodfoundation/select2-rails", branch: "v349_with_thor_v1"
gem 'good_migrations'
@@ -187,19 +182,16 @@ group :test do
end
group :development do
gem 'debugger-linecache'
gem 'foreman'
gem 'haml_lint', require: false
gem 'i18n-tasks'
gem 'listen'
gem 'pry'
gem 'pry', '~> 0.13.0'
gem 'query_count'
gem 'rails-erd'
gem 'rubocop'
gem 'rubocop-capybara'
gem 'rubocop-factory_bot'
gem 'rubocop-rails'
gem 'rubocop-rspec'
gem 'rubocop-rspec_rails'
gem 'spring'
gem 'spring-commands-rspec'
gem 'spring-commands-rubocop'

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
# Foreman Procfile. Start all dev server processes with: `foreman start`
rails: DEV_CACHING=true bundle exec rails s -p 3000
webpack: ./bin/shakapacker-dev-server
webpack: ./bin/webpack-dev-server
sidekiq: DEV_CACHING=true bundle exec sidekiq -q mailers -q default

View File

@@ -1,5 +1,5 @@
# Foreman Procfile for Docker env. Start all dev server processes with: `bundle exec foreman start -f Procfile.docker`
webpack: SHAKAPACKER_DEV_SERVER_HOST=0.0.0.0 ./bin/shakapacker-dev-server
webpack: WEBPACKER_DEV_SERVER_HOST=0.0.0.0 ./bin/webpack-dev-server
sidekiq: DEV_CACHING=true bundle exec sidekiq -q mailers -q default
rails: SHAKAPACKER_DEV_SERVER_HOST=0.0.0.0 DEV_CACHING=true bundle exec rails s -p 3000 -b 0.0.0.0
rails: WEBPACKER_DEV_SERVER_HOST=0.0.0.0 DEV_CACHING=true bundle exec rails s -p 3000 -b 0.0.0.0

View File

@@ -11,9 +11,6 @@ angular.module("admin.customers").controller "customersCtrl", ($scope, $q, $filt
$scope.confirmRefresh = (event) ->
event.preventDefault() unless pendingChanges.unsavedCount() == 0 || confirm(t("unsaved_changes_warning"))
$scope.hasUnsavedChanges = ->
pendingChanges.yes()
$scope.$watch "shop_id", ->
if $scope.shop_id?
CurrentShop.shop = $filter('filter')($scope.shops, {id: parseInt($scope.shop_id)}, true)[0]

View File

@@ -6,6 +6,7 @@ angular.module("admin.enterprises", [
"admin.side_menu",
"admin.taxons",
'admin.indexUtils',
'admin.tagRules',
'admin.dropdown',
'ngSanitize']
)

View File

@@ -4,16 +4,15 @@ angular.module("admin.indexUtils").directive "objForUpdate", (switchClass, pendi
type: "@objForUpdate"
attr: "@attrForUpdate"
link: (scope, element, attrs) ->
scope.savedValue = scope.object()[scope.attr] || ""
scope.savedValue = scope.object()[scope.attr]
scope.$watch "object().#{scope.attr}", (value) ->
strValue = value || ""
if strValue == scope.savedValue
if value == scope.savedValue
pendingChanges.remove(scope.object().id, scope.attr)
scope.clear()
else
scope.pending()
addPendingChange(scope.attr, strValue)
addPendingChange(scope.attr, value ? "")
scope.reset = (value) ->
scope.savedValue = value
@@ -38,13 +37,16 @@ angular.module("admin.indexUtils").directive "objForUpdate", (switchClass, pendi
# To ensure the customer is still updated, we check on the $destroy event to see if
# the attribute has changed, if so we queue up the change.
scope.$on '$destroy', (value) ->
currentValue = scope.object()[scope.attr] || ""
# No update
return if currentValue is scope.savedValue
return if scope.object()[scope.attr] is scope.savedValue
# For some reason the code attribute is removed from the object when cleared, so we add
# an emptyvalue so it gets updated properly
if scope.attr is "code" and scope.object()[scope.attr] is undefined
scope.object()["code"] = ""
# Queuing up change
addPendingChange(scope.attr, currentValue)
addPendingChange(scope.attr, scope.object()[scope.attr])
# private

View File

@@ -16,10 +16,7 @@ angular.module("admin.indexUtils").factory "pendingChanges", ($q, resources, Sta
remove: (id, attr) =>
if @pendingChanges.hasOwnProperty("#{id}")
delete @pendingChanges["#{id}"]["#{attr}"]
if @changeCount( @pendingChanges["#{id}"] ) < 1
delete @pendingChanges["#{id}"]
StatusMessage.clear()
delete @pendingChanges["#{id}"] if @changeCount( @pendingChanges["#{id}"] ) < 1
submitAll: (form=null) =>
all = []
@@ -50,8 +47,5 @@ angular.module("admin.indexUtils").factory "pendingChanges", ($q, resources, Sta
unsavedCount: ->
Object.keys(@pendingChanges).length
yes: ->
@unsavedCount() > 0
changeCount: (objectChanges) ->
Object.keys(objectChanges).length

View File

@@ -1,4 +1,4 @@
angular.module("admin.indexUtils").factory "switchClass", ($timeout, StatusMessage) ->
angular.module("admin.indexUtils").factory "switchClass", ($timeout) ->
return (element, classToAdd, removeClasses, timeout) ->
$timeout.cancel element.timeout if element.timeout
element.removeClass className for className in removeClasses
@@ -7,6 +7,4 @@ angular.module("admin.indexUtils").factory "switchClass", ($timeout, StatusMessa
if timeout && intRegex.test(timeout)
element.timeout = $timeout(->
element.removeClass classToAdd
StatusMessage.clear()
, timeout, true)
element

View File

@@ -0,0 +1,58 @@
angular.module("admin.tagRules").controller "TagRulesCtrl", ($scope, $http, $filter, enterprise) ->
$scope.tagGroups = enterprise.tag_groups
$scope.defaultTagGroup = enterprise.default_tag_group
$scope.visibilityOptions = [ { id: "visible", name: t('js.tag_rules.visible') }, { id: "hidden", name: t('js.tag_rules.not_visible') } ]
$scope.updateRuleCounts = ->
index = $scope.defaultTagGroup.rules.length
for tagGroup in $filter('orderBy')($scope.tagGroups, 'position')
tagGroup.startIndex = index
index = index + tagGroup.rules.length
$scope.updateRuleCounts()
$scope.updateTagsRulesFor = (tagGroup) ->
for tagRule in tagGroup.rules
tagRule.preferred_customer_tags = (tag.text for tag in tagGroup.tags).join(",")
$scope.addNewRuleTo = (tagGroup, ruleType) ->
newRule =
id: null
is_default: tagGroup == $scope.defaultTagGroup
preferred_customer_tags: (tag.text for tag in tagGroup.tags).join(",")
type: "TagRule::#{ruleType}"
switch ruleType
when "FilterShippingMethods"
newRule.peferred_shipping_method_tags = []
newRule.preferred_matched_shipping_methods_visibility = "visible"
when "FilterPaymentMethods"
newRule.peferred_payment_method_tags = []
newRule.preferred_matched_payment_methods_visibility = "visible"
when "FilterProducts"
newRule.peferred_variant_tags = []
newRule.preferred_matched_variants_visibility = "visible"
when "FilterOrderCycles"
newRule.peferred_exchange_tags = []
newRule.preferred_matched_order_cycles_visibility = "visible"
tagGroup.rules.push(newRule)
$scope.updateRuleCounts()
$scope.addNewTag = ->
$scope.tagGroups.push { tags: [], rules: [], position: $scope.tagGroups.length + 1 }
$scope.deleteTagRule = (tagGroup, tagRule) ->
index = tagGroup.rules.indexOf(tagRule)
return unless index >= 0
if tagRule.id is null
tagGroup.rules.splice(index, 1)
$scope.updateRuleCounts()
else
if confirm("Are you sure?")
$http
method: "DELETE"
url: "/admin/enterprises/#{enterprise.id}/tag_rules/#{tagRule.id}.json"
.then ->
tagGroup.rules.splice(index, 1)
$scope.updateRuleCounts()
$scope.enterprise_form.$setDirty()

View File

@@ -0,0 +1,11 @@
angular.module("admin.tagRules").directive "invertNumber", ->
restrict: "A"
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
ngModel.$parsers.push (viewValue) ->
return -parseInt(viewValue) unless isNaN(parseInt(viewValue))
viewValue
ngModel.$formatters.push (modelValue) ->
return -parseInt(modelValue) unless isNaN(parseInt(modelValue))
modelValue

View File

@@ -0,0 +1,26 @@
angular.module("admin.tagRules").directive 'newTagRuleDialog', ($rootScope, $compile, $templateCache, DialogDefaults, ruleTypes) ->
restrict: 'A'
scope:
tagGroup: '='
addNewRuleTo: '='
link: (scope, element, attr) ->
# Compile modal template
template = $compile($templateCache.get('admin/new_tag_rule_dialog.html'))(scope)
scope.ruleTypes = ruleTypes
scope.ruleType = scope.ruleTypes[0].id
# Set Dialog options
template.dialog(DialogDefaults)
# Link opening of dialog to click event on element
element.bind 'click', (e) ->
template.dialog('open')
$rootScope.$evalAsync()
scope.addRule = (tagGroup, ruleType) ->
scope.addNewRuleTo(tagGroup, ruleType)
template.dialog('close')
$rootScope.$evalAsync()
return

View File

@@ -0,0 +1,41 @@
angular.module("admin.tagRules").directive "tagRule", ->
restrict: "C"
templateUrl: "admin/tag_rules/tag_rule.html"
link: (scope, element, attrs) ->
scope.opt =
"TagRule::FilterShippingMethods":
textTop: t('js.admin.tag_rules.shipping_method_tagged_top')
textBottom: t('js.admin.tag_rules.shipping_method_tagged_bottom')
taggable: "shipping_method"
tagsAttr: "shipping_method_tags"
tagListAttr: "preferred_shipping_method_tags"
inputTemplate: "admin/tag_rules/filter_shipping_methods_input.html"
tagListFor: (rule) ->
rule.preferred_shipping_method_tags
"TagRule::FilterPaymentMethods":
textTop: t('js.admin.tag_rules.payment_method_tagged_top')
textBottom: t('js.admin.tag_rules.payment_method_tagged_bottom')
taggable: "payment_method"
tagsAttr: "payment_method_tags"
tagListAttr: "preferred_payment_method_tags"
inputTemplate: "admin/tag_rules/filter_payment_methods_input.html"
tagListFor: (rule) ->
rule.preferred_payment_method_tags
"TagRule::FilterOrderCycles":
textTop: t('js.admin.tag_rules.order_cycle_tagged_top')
textBottom: t('js.admin.tag_rules.order_cycle_tagged_bottom')
taggable: "exchange"
tagsAttr: "exchange_tags"
tagListAttr: "preferred_exchange_tags"
inputTemplate: "admin/tag_rules/filter_order_cycles_input.html"
tagListFor: (rule) ->
rule.preferred_exchange_tags
"TagRule::FilterProducts":
textTop: t('js.admin.tag_rules.inventory_tagged_top')
textBottom: t('js.admin.tag_rules.inventory_tagged_bottom')
taggable: "variant"
tagsAttr: "variant_tags"
tagListAttr: "preferred_variant_tags"
inputTemplate: "admin/tag_rules/filter_products_input.html"
tagListFor: (rule) ->
rule.preferred_variant_tags

View File

@@ -0,0 +1,2 @@
angular.module('Darkswarm').controller "TmpCtrl", ($scope)->
$scope.test = {foo: "bar"}

View File

@@ -1,6 +1,6 @@
angular.module('Darkswarm').directive "activeTableHubLink", (CurrentHub, CurrentOrder) ->
# Change the text of the hub link based on CurrentHub
# To be used with ofnChangeHub
# To be used with ofnEmptiesCart
# Takes "change" and "shop" as text string attributes
restrict: "A"
scope:

View File

@@ -0,0 +1,11 @@
angular.module('Darkswarm').directive "darkerBackground", ->
restrict: "A"
link: (scope, elm, attr)->
toggleClass = (value) ->
elm.closest('.page-view').toggleClass("with-darker-background", value)
toggleClass(true)
# if an OrderCycle is selected, disable darker background
scope.$watch 'order_cycle.order_cycle_id', (newvalue, oldvalue) ->
toggleClass(false) if newvalue

View File

@@ -0,0 +1,14 @@
# Allows disabling of link buttons via disabled attribute.
# This is normally ignored, ie the link appears disabled but is still clickable.
angular.module('Darkswarm').directive "disableDynamically", ->
restrict: 'A'
link: (scope, element, attrs) ->
element.on 'click', (e) ->
if attrs.disabled
e.preventDefault()
return
scope.$on "$destroy", ->
element.off("click")

View File

@@ -0,0 +1,7 @@
angular.module('Darkswarm').directive "ofnInlineAlert", ->
restrict: 'A'
scope: true
link: (scope, elem, attrs) ->
scope.visible = true
scope.close = ->
scope.visible = false

View File

@@ -0,0 +1,21 @@
angular.module('Darkswarm').directive "ofnPageAlert", ($timeout) ->
restrict: 'A'
scope: true
link: (scope, elem, attrs) ->
moveSelectors = [".off-canvas-wrap .inner-wrap",
".off-canvas-wrap .inner-wrap .fixed",
".off-canvas-fixed .top-bar",
".off-canvas-fixed ofn-flash",
".off-canvas-fixed nav.tab-bar",
".off-canvas-fixed .page-alert"]
container_elems = $(moveSelectors.join(", "))
# Wait a moment after page load before showing the alert. Otherwise we often miss the
# start of the animation.
$timeout ->
container_elems.addClass("move-up")
, 1000
scope.close = ->
container_elems.removeClass("move-up")

View File

@@ -0,0 +1,10 @@
#new-tag-rule-dialog
.text-normal.margin-bottom-30.text-center
{{ 'js.admin.new_tag_rule_dialog.select_rule_type' | t }}
.text-center.margin-bottom-30
-# %select.fullwidth{ 'select2-min-search' => 5, 'ng-model' => 'newRuleType', 'ng-options' => 'ruleType.id as ruleType.name for ruleType in availableRuleTypes' }
%input.ofn-select2.fullwidth{ id: 'rule_type_selector', data: "ruleTypes", "min-search": "5", "ng-model": "ruleType" }
.text-center
%input.button.red.icon-plus{ type: 'button', value: "{{ 'js.admin.new_tag_rule_dialog.add_rule' | t }}", "ng-click": 'addRule(tagGroup, ruleType)' }

View File

@@ -3,4 +3,4 @@
%span.text-normal
{{ 'admin.tags' | t }}
%br
%tags-with-translation.fullwidth{ object: 'object', form: 'order_cycle_form', id: 'tags_with_translation'}
%tags-with-translation.fullwidth{ object: 'object' }

View File

@@ -0,0 +1,3 @@
%div
%input{ type: "number", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent", min: -100, max: 100, "invert-number": true, "ng-model": "rule.calculator.preferred_flat_percent" }
%span.text-normal %

View File

@@ -0,0 +1,5 @@
%div
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_order_cycles_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_order_cycles_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_order_cycles_visibility", "ng-if": "!rule.is_default" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_order_cycles_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_order_cycles_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" }
%span.text-normal{ "ng-if": "rule.is_default" }
=t(:not_visible)

View File

@@ -0,0 +1,5 @@
%div
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_payment_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_payment_methods_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_payment_methods_visibility", "ng-if": "!rule.is_default" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_payment_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_payment_methods_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" }
%span.text-normal{ "ng-if": "rule.is_default" }
= t(:not_visible)

View File

@@ -0,0 +1,5 @@
%div
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_variants_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_variants_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_variants_visibility", "ng-if": "!rule.is_default" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_variants_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_variants_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" }
%span.text-normal{ "ng-if": "rule.is_default" }
= t(:not_visible)

View File

@@ -0,0 +1,6 @@
%div
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_shipping_methods_visibility", "ng-if": "!rule.is_default" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" }
%span.text-normal{ "ng-if": "rule.is_default" }
= t(:not_visible)

View File

@@ -0,0 +1,32 @@
%div{ id: "tr_{{tagGroup.startIndex + $index}}" }
%table
%colgroup
%col.text{ width: "35%" }
%col.inputs{ width: "55%" }
%col.actions{ width: "10%" }
%tr
%td
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_id", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][id]", "ng-value": "rule.id" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_type", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][type]", "ng-value": "rule.type" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_priority", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][priority]", "ng-value": "tagGroup.startIndex + $index" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_is_default", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][is_default]", "ng-value": "rule.is_default" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_customer_tags", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_customer_tags]", "ng-value": "rule.preferred_customer_tags" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_{{opt[rule.type].taggable}}_tags", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_{{opt[rule.type].taggable}}_tags]", "ng-value": "opt[rule.type].tagListFor(rule)" }
%span.text-normal {{ opt[rule.type].textTop }}
%td
%tags-with-translation{ object: "rule", max: 1, "tags-attr" => "{{opt[rule.type].tagsAttr}}", "tag-list-attr" => "{{opt[rule.type].tagListAttr}}" }
%td.actions{ rowspan: 2 }
%a{ class: "delete-tag-rule icon-trash no-text", "ng-click": "deleteTagRule(tagGroup || defaultTagGroup, rule)" }
%tr
%td
%span.text-normal {{ opt[rule.type].textBottom }}
%td
%div{ "ng-include": "opt[rule.type].inputTemplate" }
%hr

View File

@@ -12,7 +12,7 @@
.row
.columns.small-12
%a.cta-hub{"ng-repeat" => "hub in enterprise.hubs | filter:{id: '!'+enterprise.id} | orderBy:'-active'",
"ng-href" => "{{::hub.path}}#/shop_panel",
"ng-href" => "{{::hub.path}}#/shop_panel", "ofn-empties-cart" => "hub",
"ng-class" => "::{primary: hub.active, secondary: !hub.active}",
"ng-click" => "$close()",
"ofn-change-hub" => "hub"}

View File

@@ -0,0 +1 @@
@import './mail/all.scss';

View File

@@ -0,0 +1,3 @@
@import '../../../webpacker/css/admin/globals/palette.scss';
@import 'email';
@import 'payments_list';

View File

@@ -1,21 +0,0 @@
# frozen_string_literal: true
class AddTagRuleModalComponent < ModalComponent
def initialize(id:, tag_rule_types:, current_index:, div_id:, is_default: false,
customer_tag: "", hidden_field_customer_tag_options: {} )
super
@close_button = false
@modal_class = "tiny"
@tag_rule_types = tag_rule_types
@current_index = current_index
@div_id = div_id
@is_default = is_default
@customer_tag = customer_tag
@hidden_field_customer_tag_options = hidden_field_customer_tag_options
end
attr_reader :tag_rule_types, :current_index, :div_id, :is_default, :customer_tag,
:hidden_field_customer_tag_options
end

View File

@@ -1,32 +0,0 @@
-# as far as I can tell we can't pass content to the parent template while rendering ie: something like :
-# = render_parent do
-# .something
-# my content
-# Workarount is to copy the ModalComponent template
%div{ id: @id, "data-controller": @data_controller, "data-action": @data_action, "data-modal-instant-value": @instant, **@options }
.reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" }
.reveal-modal.fade.modal-component{ "data-modal-target": "modal", class: @modal_class }
#new-tag-rule-dialog{ "data-controller": "add-tag-rule-modal",
"data-add-tag-rule-modal-index-value": current_index }
-# Ideally we would use event to communicate the update of customer tag, but we would need
-# the element with "data-controller": "add-tag-rule-modal"
-# to be parent of the element with "data-controller": "tag-rule-group-form"
-# so it could respond to event generated by "tag-rule-group-form".
-# Here we are in the opposite situation so we use a hidden field to store the value of
-# the customer tag, so it can be updated by "tag-rule-group-form"
= hidden_field_tag "customer_tag", customer_tag, { "data-add-tag-rule-modal-target": "ruleCustomerTag" }.merge(hidden_field_customer_tag_options)
.text-normal.margin-bottom-30.text-center
= t('components.add_tag_rule_modal.select_rule_type')
.text-center.margin-bottom-30
= select_tag :rule_type_selector, options_for_select(tag_rule_types), { "data-controller": "tom-select", "data-add-tag-rule-modal-target": "rule", class: "primary no-search" }
.text-center
%input.button.red.icon-plus{ type: 'button',
value: "#{t('components.add_tag_rule_modal.add_rule')}",
"data-action": "click->add-tag-rule-modal#add click->modal#close",
"data-add-tag-rule-modal-div-id-param": div_id,
"data-add-tag-rule-modal-is-default-param": "#{is_default}"}
- if close_button?
.text-center
%input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.close'), "data-action": "click->modal#close" }

View File

@@ -1,46 +0,0 @@
import { Controller } from "stimulus";
import showHttpError from "../../webpacker/js/services/show_http_error";
export default class extends Controller {
static targets = ["rule", "ruleCustomerTag"];
static values = { index: Number };
add({ params }) {
const rule_type = this.ruleTarget.value;
const index = this.indexValue;
const divId = params["divId"];
const isDefault = params["isDefault"];
const customerTags = this.hasRuleCustomerTagTarget
? this.ruleCustomerTagTarget.value
: undefined;
const urlParams = new URLSearchParams();
urlParams.append("rule_type", rule_type);
urlParams.append("index", index);
urlParams.append("div_id", divId);
urlParams.append("is_default", isDefault);
if (customerTags != undefined) {
urlParams.append("customer_tags", customerTags);
}
// fetch from backend
fetch(`tag_rules/new?${urlParams}`, {
method: "GET",
headers: {
Accept: "text/vnd.turbo-stream.html",
},
})
.then((response) => {
if (!response.ok) {
showHttpError(response.status);
throw response;
}
return response.text();
})
.then((html) => {
Turbo.renderStreamMessage(html);
this.indexValue = parseInt(index) + 1;
})
.catch((error) => console.error(error));
}
}

View File

@@ -1,5 +1,4 @@
// This controller will be called "example", ie "js-file-name" minus the "_controller.js"
// see controller/index.js for more info
// This controller will be called "example-component--example", ie "component-subdirectory--js-file-name"
import { Controller } from "stimulus";
export default class extends Controller {}

View File

@@ -1,18 +1,17 @@
# frozen_string_literal: true
class SearchableDropdownComponent < ViewComponent::Base
REMOVED_SEARCH_PLUGIN = { 'tom-select-options-value': '{ "plugins": [] }' }.freeze
MINIMUM_OPTIONS_FOR_SEARCH_FIELD = 11 # at least 11 options are required for the search field
def initialize(
form:,
name:,
options:,
selected_option:,
form: nil,
placeholder_value: '',
placeholder_value:,
include_blank: false,
aria_label: '',
multiple: false,
remote_url: nil,
other_attrs: {}
)
@f = form
@@ -22,50 +21,26 @@ class SearchableDropdownComponent < ViewComponent::Base
@placeholder_value = placeholder_value
@include_blank = include_blank
@aria_label = aria_label
@multiple = multiple
@remote_url = remote_url
@other_attrs = other_attrs
end
private
attr_reader :f, :name, :options, :selected_option, :placeholder_value, :include_blank,
:aria_label, :multiple, :remote_url, :other_attrs
:aria_label, :other_attrs
def classes
"fullwidth #{'no-input' if remove_search_plugin?}"
"fullwidth #{remove_search_plugin? ? 'no-input' : ''}"
end
def data
{
controller: "tom-select",
'tom-select-placeholder-value': placeholder_value,
'tom-select-options-value': tom_select_options_value,
'tom-select-remote-url-value': remote_url,
}
end
def tom_select_options_value
plugins = []
plugins << 'virtual_scroll' if @remote_url.present?
plugins << 'dropdown_input' unless remove_search_plugin?
plugins << 'remove_button' if multiple
{
plugins:,
maxItems: multiple ? nil : 1,
}
end
def uses_form_builder?
f.present?
'tom-select-placeholder-value': placeholder_value
}.merge(remove_search_plugin? ? REMOVED_SEARCH_PLUGIN : {})
end
def remove_search_plugin?
# Remove the search plugin when:
# - the select is multiple (it already includes a search field), or
# - there is no remote URL and the options are below the search threshold
@remove_search_plugin ||= multiple ||
(@remote_url.nil? && options.count < MINIMUM_OPTIONS_FOR_SEARCH_FIELD)
@remove_search_plugin ||= options.count < MINIMUM_OPTIONS_FOR_SEARCH_FIELD
end
end

View File

@@ -1,4 +1 @@
- if uses_form_builder?
= f.select name, options, { selected: selected_option, include_blank:, multiple: }, class: classes, data:, 'aria-label': aria_label, **other_attrs
- else
= select_tag name, options_for_select(options, selected_option), include_blank:, multiple:, class: classes, data:, 'aria-label': aria_label, **other_attrs
= f.select name, options_for_select(options, selected_option), { include_blank: }, class: classes, data:, 'aria-label': aria_label, **other_attrs

View File

@@ -1,4 +1,4 @@
= render ConfirmModalComponent.new(id: dom_id(@order, :ship), confirm_actions: "click->modal#close", confirm_reflexes: "click->Admin::OrdersReflex#ship", controller: "orders", reflex: "Admin::Orders#ship") do
= render ConfirmModalComponent.new(id: dom_id(@order, :ship), confirm_reflexes: "click->Admin::OrdersReflex#ship", controller: "orders", reflex: "Admin::Orders#ship") do
%div{class: "margin-bottom-30"}
%p= t('spree.admin.orders.shipment.mark_as_shipped_message_html')
%div{class: "margin-bottom-30"}

View File

@@ -1,29 +1,16 @@
# frozen_string_literal: true
class TagListInputComponent < ViewComponent::Base
def initialize(name:, tags:,
# method in a "hidden_field" form helper and is the method used to get a list of tag on the model
def initialize(form:, method:, tags:,
placeholder: I18n.t("components.tag_list_input.default_placeholder"),
only_one: false,
aria_label: nil,
hidden_field_data_options: {},
autocomplete_url: "")
@name = name
aria_label: nil)
@f = form
@method = method
@tags = tags
@placeholder = placeholder
@only_one = only_one
@aria_label_option = aria_label ? { 'aria-label': aria_label } : {}
@hidden_field_data_options = hidden_field_data_options
@autocomplete_url = autocomplete_url
end
attr_reader :name, :tags, :placeholder, :only_one, :aria_label_option,
:hidden_field_data_options, :autocomplete_url
private
def display
return "none" if tags.length >= 1 && only_one == true
"block"
end
attr_reader :f, :method, :tags, :placeholder, :aria_label_option
end

View File

@@ -1,27 +1,19 @@
%div{ "data-controller": "tag-list-input", "data-tag-list-input-only-one-value": "#{only_one}", "data-tag-list-input-url-value": autocomplete_url, "data-action": "autocomplete.change->tag-list-input#addTag" }
%div{ "data-controller": "tag-list-input-component--tag-list-input" }
.tags-input
.tags
- # We use display:none instead of hidden field, so changes to the value can be picked up by the bulkFormController
= text_field_tag name, tags.join(","), {"data-tag-list-input-target": "tagList", "style": "display: none"}.merge(hidden_field_data_options)
%ul.tag-list{"data-tag-list-input-target": "list"}
%template{"data-tag-list-input-target": "template"}
= f.text_field method.to_sym, value: tags.join(","), "data-tag-list-input-component--tag-list-input-target": "tagList", "style": "display: none"
%ul.tag-list{"data-tag-list-input-component--tag-list-input-target": "list"}
%template{"data-tag-list-input-component--tag-list-input-target": "template"}
%li.tag-item
.tag-template
%span
%a.remove-button{ "data-action": "click->tag-list-input#removeTag" }
%a.remove-button{ "data-action": "click->tag-list-input-component--tag-list-input#removeTag" }
×
- tags.each do |tag|
%li.tag-item
.tag-template
%span=tag
%a.remove-button{ "data-action": "click->tag-list-input#removeTag" }
%a.remove-button{ "data-action": "click->tag-list-input-component--tag-list-input#removeTag" }
×
= text_field_tag("variant_add_tag",
nil,
{ class: "input",
placeholder: placeholder,
"data-action": "keydown.enter->tag-list-input#keyboardAddTag keyup->tag-list-input#filterInput blur->tag-list-input#onBlur focus->tag-list-input#onInputChange",
"data-tag-list-input-target": "input",
**aria_label_option,
style: "display: #{display};"})
%ul.suggestion-list{ "data-tag-list-input-target": "results" , hidden: true }
= text_field_tag "variant_add_tag_#{f.object.id}".to_sym, nil, class: "input", placeholder: placeholder, "data-action": "keydown.enter->tag-list-input-component--tag-list-input#addTag keyup->tag-list-input-component--tag-list-input#filterInput", "data-tag-list-input-component--tag-list-input-target": "newTag", **aria_label_option

View File

@@ -67,35 +67,4 @@
font-size: 14px;
}
}
ul.suggestion-list {
margin-top: 5px;
padding: 5px 0;
z-index: $tag-drop-down-z-index;
width: fit-content;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: $shadow-dropdown;
list-style-type: none;
max-height: 280px;
overflow-y: auto;
position: relative;
li.suggestion-item {
padding: 5px 10px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #000;
background-color: #fff;
width: stretch;
&.active,
&:hover {
color: #fff;
background-color: $color-link-visited;
}
}
}
}

View File

@@ -1,44 +1,28 @@
import { Autocomplete } from "stimulus-autocomplete";
import { Controller } from "stimulus";
// Extend the stimulus-autocomplete controller, so we can load tag with existing rules
// The autocomplete functionality is only loaded if the url value is set
// For more informatioon on "stimulus-autocomplete", see:
// https://github.com/afcapel/stimulus-autocomplete/tree/main
//
export default class extends Autocomplete {
static targets = ["tagList", "input", "template", "list"];
static values = { onlyOne: Boolean };
connect() {
// Don't start autocomplete controller if we don't have an url
if (this.urlValue.length == 0) return;
super.connect();
}
export default class extends Controller {
static targets = ["tagList", "newTag", "template", "list"];
addTag(event) {
const newTagName = this.inputTarget.value.trim().replaceAll(" ", "-");
// prevent hotkey form submitting the form (default action for "enter" key)
event.preventDefault();
// Check if tag already exist
const newTagName = this.newTagTarget.value.trim();
if (newTagName.length == 0) {
return;
}
// Check if tag already exist
const tags = this.tagListTarget.value.split(",");
const index = tags.indexOf(newTagName);
if (index != -1) {
// highlight the value in red
this.inputTarget.classList.add("tag-error");
this.newTagTarget.classList.add("tag-error");
return;
}
// add to tagList
if (this.tagListTarget.value == "") {
this.tagListTarget.value = newTagName;
} else {
this.tagListTarget.value = this.tagListTarget.value.concat(`,${newTagName}`);
}
// manualy dispatch an Input event so the change can get picked up by other controllers
this.tagListTarget.dispatchEvent(new InputEvent("input"));
this.tagListTarget.value = this.tagListTarget.value.concat(`,${newTagName}`);
// Create new li component with value
const newTagElement = this.templateTarget.content.cloneNode(true);
@@ -47,21 +31,7 @@ export default class extends Autocomplete {
this.listTarget.appendChild(newTagElement);
// Clear new tag value
this.inputTarget.value = "";
// hide tag input if limited to one tag
if (this.tagListTarget.value.split(",").length == 1 && this.onlyOneValue == true) {
this.inputTarget.style.display = "none";
}
}
keyboardAddTag(event) {
// prevent hotkey form submitting the form (default action for "enter" key)
if (event) {
event.preventDefault();
}
this.addTag();
this.newTagTarget.value = "";
}
removeTag(event) {
@@ -70,24 +40,19 @@ export default class extends Autocomplete {
// Remove tag from list
const tags = this.tagListTarget.value.split(",");
this.tagListTarget.value = tags.filter((tag) => tag != tagName).join(",");
this.tagListTarget.value = tags.filter(tag => tag != tagName).join(",");
// manualy dispatch an Input event so the change gets picked up by the bulk form controller
this.tagListTarget.dispatchEvent(new InputEvent("input"));
// Remove HTML element from the list
event.srcElement.parentElement.parentElement.remove();
// Make sure the tag input is displayed
if (this.tagListTarget.value.length == 0) {
this.inputTarget.style.display = "block";
}
}
filterInput(event) {
// clear error class if key is not enter
if (event.key !== "Enter") {
this.inputTarget.classList.remove("tag-error");
this.newTagTarget.classList.remove("tag-error");
}
// Strip comma from tag name
@@ -95,53 +60,4 @@ export default class extends Autocomplete {
event.srcElement.value = event.srcElement.value.replace(",", "");
}
}
// Add tag if we don't have an autocomplete list open
onBlur() {
// check if we have any autocomplete results
if (this.resultsTarget.childElementCount == 0) this.addTag();
}
// Override original to add tag filtering
replaceResults(html) {
const filteredHtml = this.#filterResults(html);
// Don't show result if we don't have anything to show
if (filteredHtml.length == 0) return;
super.replaceResults(filteredHtml);
}
// Override original to all empty query, which will return all existing tags
onInputChange = () => {
if (this.urlValue.length == 0) return;
if (this.hasHiddenTarget) this.hiddenTarget.value = "";
const query = this.inputTarget.value.trim();
if (query.length >= this.minLengthValue) {
this.fetchResults(query);
} else {
this.hideAndRemoveOptions();
}
};
//private
#filterResults(html) {
const existingTags = this.tagListTarget.value.split(",");
// Parse the HTML
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
const lis = doc.getElementsByTagName("li");
// Filter
let filteredHtml = "";
for (let li of lis) {
if (!existingTags.includes(li.dataset.autocompleteValue)) {
filteredHtml += li.outerHTML;
}
}
return filteredHtml;
}
}

View File

@@ -1,66 +0,0 @@
# frozen_string_literal: true
class TagRuleFormComponent < ViewComponent::Base
def initialize(rule:, index:, customer_tags: "",
hidden_field_customer_tag_options: {})
@rule = rule
@index = index
@customer_tags = customer_tags
@hidden_field_customer_tag_options = hidden_field_customer_tag_options
end
attr_reader :rule, :index, :customer_tags, :hidden_field_customer_tag_options
private
def element_name(name)
"enterprise[tag_rules_attributes][#{index}][#{name}]"
end
def rule_data # rubocop:disable Metrics/MethodLength
case rule.type
when "TagRule::FilterShippingMethods"
{
text_top: t('components.tag_rule_form.tag_rules.shipping_method_tagged_top'),
text_bottom: t('components.tag_rule_form.tag_rules.shipping_method_tagged_bottom'),
taggable: "shipping_method",
visibility_field: "preferred_matched_shipping_methods_visibility",
}
when "TagRule::FilterPaymentMethods"
{
text_top: t('components.tag_rule_form.tag_rules.payment_method_tagged_top'),
text_bottom: t('components.tag_rule_form.tag_rules.payment_method_tagged_bottom'),
taggable: "payment_method",
visibility_field: "preferred_matched_payment_methods_visibility",
}
when "TagRule::FilterOrderCycles"
{
text_top: t('components.tag_rule_form.tag_rules.order_cycle_tagged_top'),
text_bottom: t('components.tag_rule_form.tag_rules.order_cycle_tagged_bottom'),
taggable: "exchange",
visibility_field: "preferred_matched_order_cycles_visibility",
}
when "TagRule::FilterProducts"
{
text_top: t('components.tag_rule_form.tag_rules.inventory_tagged_top'),
text_bottom: t('components.tag_rule_form.tag_rules.inventory_tagged_bottom'),
taggable: "variant",
visibility_field: "preferred_matched_variants_visibility",
}
when "TagRule::FilterVariants"
{
text_top: t('components.tag_rule_form.tag_rules.variant_tagged_top'),
text_bottom: t('components.tag_rule_form.tag_rules.variant_tagged_bottom'),
taggable: "variant",
visibility_field: "preferred_matched_variants_visibility",
}
end
end
def visibility_options
[
[t('components.tag_rule_form.tag_rules.visible'), "visible"],
[t('components.tag_rule_form.tag_rules.not_visible'), "hidden"]
]
end
end

View File

@@ -1,35 +0,0 @@
%div{ id: "tr_#{index}" }
%table
%colgroup
%col.text{ width: "35%" }
%col.inputs{ width: "55%" }
%col.actions{ width: "10%" }
%tr
%td
= hidden_field_tag element_name("id"), rule.id
= hidden_field_tag element_name("type"), rule.type
= hidden_field_tag element_name("priority"), index
= hidden_field_tag element_name("is_default"), rule.is_default
= hidden_field_tag element_name("preferred_customer_tags"), customer_tags, hidden_field_customer_tag_options
%span.text-normal
= rule_data[:text_top]
%td
= render TagListInputComponent.new(name: element_name("preferred_#{rule_data[:taggable]}_tags"), tags: rule.tags.split(","), only_one: true)
%td.actions{ rowspan: 2 , "data-controller": "delete-tag-rule", "data-delete-tag-rule-index-value": index }
- if rule.new_record?
= link_to("", "#", { "data-action": "click->delete-tag-rule#delete" ,class: "delete-tag-rule icon-trash no-text"})
- else
= link_to("", "#{admin_enterprise_tag_rule_url(rule.enterprise_id, rule.id)}?index=#{index}", { "data-turbo-method": "delete", "data-turbo-confirm": t("admin.tag_rules.confirm_delete"), class: "delete-tag-rule icon-trash no-text" })
%tr
%td
%span.text-normal
= rule_data[:text_bottom]
%td
%div
%div
- if rule.is_default
= hidden_field_tag "enterprise[tag_rules_attributes][#{index}][#{rule_data[:visibility_field]}]", "hidden"
%span.text-normal
= t(:not_visible)
- else
= select_tag "enterprise[tag_rules_attributes][#{index}][#{rule_data[:visibility_field]}]", options_for_select(visibility_options, rule.public_send(rule_data[:visibility_field].to_sym) ), { "data-controller": "tom-select", class: "primary no-search" }

View File

@@ -1,26 +0,0 @@
# frozen_string_literal: true
class TagRuleGroupFormComponent < ViewComponent::Base
def initialize(group:, index:, customer_rule_index:, tag_rule_types:)
@group = group
@index = index
@customer_rule_index = customer_rule_index
@tag_rule_types = tag_rule_types
end
attr_reader :group, :index, :customer_rule_index, :tag_rule_types
private
def form_id
"tg_#{index}"
end
def customer_tag_rule_div_id
"new-customer-tag-rule-#{index}"
end
def tag_list_input_name
"group[#{index}][preferred_customer_tags]"
end
end

View File

@@ -1,39 +0,0 @@
%div{ id: form_id, "data-controller": "tag-rule-group-form" }
- rule_index = customer_rule_index
.customer_tag
.header
%table
%colgroup
%col{width: '35%'}
%col{width: '65%'}
%tr
%td
%h5
= t('components.tag_rule_group_form.for_customers_tagged')
%td
= render(TagListInputComponent.new(name: tag_list_input_name,
tags: group[:tags],
only_one: true,
hidden_field_data_options: { "data-action": "input->tag-rule-group-form#updatePreferredCustomerTag", "data-tag-rule-group-form-target": "customerTag" }))
%div{ id: customer_tag_rule_div_id }
- if group[:rules].empty?
.no_rules
= t('components.tag_rule_group_form.no_rules_yet')
- else
- group[:rules].each do |rule|
- rule_index += 1
= render(TagRuleFormComponent.new(rule: rule,
index: rule_index,
customer_tags: group[:tags],
hidden_field_customer_tag_options: { "data-tag-rule-group-form-target": "ruleCustomerTag" }))
%hr
.add_rule.text-center
%input.button{ type: 'button', value: t('components.tag_rule_group_form.add_new_rule'), "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "tag_rule_add_new_rule_#{index}" }
= render AddTagRuleModalComponent.new(id: "tag_rule_add_new_rule_#{index}",
tag_rule_types: tag_rule_types,
current_index: (rule_index + 1),
div_id: customer_tag_rule_div_id,
customer_tag: group[:tags],
hidden_field_customer_tag_options: { "data-tag-rule-group-form-target": "ruleCustomerTag" })

View File

@@ -1,11 +0,0 @@
import { Controller } from "stimulus";
export default class extends Controller {
static targets = ["customerTag", "ruleCustomerTag"];
updatePreferredCustomerTag() {
const customerTag = this.customerTagTarget.value;
this.ruleCustomerTagTargets.forEach((element) => (element.value = customerTag));
}
}

View File

@@ -1,4 +1,4 @@
.vertical-ellipsis-menu{ "data-controller": "vertical-ellipsis-menu" }
%i.fa.fa-ellipsis-v{ "data-action": "click->vertical-ellipsis-menu#toggle" }
.vertical-ellipsis-menu-content{ "data-vertical-ellipsis-menu-target": "content" }
.vertical-ellipsis-menu{ "data-controller": "vertical-ellipsis-menu--component" }
%i.fa.fa-ellipsis-v{ "data-action": "click->vertical-ellipsis-menu--component#toggle" }
.vertical-ellipsis-menu-content{ "data-vertical-ellipsis-menu--component-target": "content" }
= content

View File

@@ -0,0 +1,6 @@
# frozen_string_literal: true
module VerticalEllipsisMenu
class Component < ViewComponent::Base
end
end

View File

@@ -1,4 +0,0 @@
# frozen_string_literal: true
class VerticalEllipsisMenuComponent < ViewComponent::Base
end

View File

@@ -1,16 +0,0 @@
# frozen_string_literal: true
class WebhookEndpointFormComponent < ViewComponent::Base
def initialize(webhooks:, webhook_type:)
@webhooks = webhooks
@webhook_type = webhook_type
end
private
attr_reader :webhooks, :webhook_type
def is_webhook_payment_status?
webhook_type == "payment_status_changed"
end
end

View File

@@ -1,27 +0,0 @@
-# Create new endpoints
- if webhooks.empty? # Only one allowed for now.
%tr
%td= t("components.webhook_endpoint_form.event_types.#{webhook_type}")
%td
= form_with(url: helpers.account_webhook_endpoints_path, id: "#{webhook_type}_webhook_endpoint") do |f|
= f.url_field :'webhook_endpoint[url]', id: "#{webhook_type}_webhook_endpoint_url", placeholder: t('components.webhook_endpoint_form.url.create_placeholder'), required: true, size: 64
= f.hidden_field :'webhook_endpoint[webhook_type]', id: "#{webhook_type}_webhook_endpoint_webhook_type", value: webhook_type
%td.actions
= button_tag t(:create), class: 'button primary tiny no-margin', form: "#{webhook_type}_webhook_endpoint"
-# Existing endpoints
- webhooks.each do |webhook_endpoint|
%tr
%td= t("components.webhook_endpoint_form.event_types.#{webhook_type}")
%td= webhook_endpoint.url
%td.actions.endpoints-actions
- if webhook_endpoint.persisted?
= button_to helpers.account_webhook_endpoint_path(webhook_endpoint), method: :delete,
class: "tiny alert no-margin",
data: { confirm: I18n.t(:are_you_sure) } do
= I18n.t(:delete)
- if is_webhook_payment_status?
= form_tag helpers.webhook_endpoint_test_account_path(webhook_endpoint), class: "button_to", 'data-turbo': true do
= button_tag type: "submit", class: "tiny alert no-margin", data: { confirm: I18n.t(:are_you_sure) } do
= I18n.t("components.webhook_endpoint_form.test_endpoint")

View File

@@ -3,7 +3,6 @@
module Admin
class BulkLineItemsController < Spree::Admin::BaseController
include PaginationData
# GET /admin/bulk_line_items.json
#
def index

View File

@@ -51,18 +51,6 @@ module Admin
end
end
# copy of Admin::ResourceController without flash notice
def update
if @object.update(permitted_resource_params)
respond_with(@object) do |format|
format.html { redirect_to location_after_save }
format.js { render layout: false }
end
else
respond_with(@object)
end
end
# copy of Admin::ResourceController without flash notice
def destroy
if @object.destroy

View File

@@ -15,8 +15,8 @@ module Admin
def index
# Fetch DFC catalog JSON for preview
@catalog_url = params.require(:catalog_url).strip
@catalog_data = api.call(@catalog_url)
catalog = DfcCatalog.from_json(@catalog_data)
@catalog_json = api.call(@catalog_url)
catalog = DfcCatalog.from_json(@catalog_json)
# Render table and let user decide which ones to import.
@items = list_products(catalog)

View File

@@ -51,7 +51,6 @@ module Admin
load_tag_rule_types
load_tag_rules
return unless params[:stimulus]
@enterprise.is_primary_producer = params[:is_primary_producer]
@@ -84,8 +83,6 @@ module Admin
format.turbo_stream
end
else
load_tag_rule_types
load_tag_rules
respond_with(@object) do |format|
format.json {
render json: { errors: @object.errors.messages }, status: :unprocessable_entity
@@ -150,30 +147,6 @@ module Admin
end
end
def new_tag_rule_group
load_tag_rule_types
@index = params[:index]
@customer_rule_index = params[:customer_rule_index].to_i
@group = { tags: [], rules: [] }
respond_to do |format|
format.turbo_stream
end
end
def destroy
if @object.destroy
flash.now[:success] = flash_message_for(@object, :successfully_removed)
else
flash.now[:error] = @object.errors.full_messages.to_sentence
end
respond_to do |format|
format.turbo_stream { render :destroy, status: :ok }
end
end
protected
def delete_custom_tab
@@ -406,32 +379,16 @@ module Admin
end
def load_tag_rule_types
# Load rule types
@tag_rule_types = [
[t(".form.tag_rules.show_hide_shipping"), "FilterShippingMethods"],
[t(".form.tag_rules.show_hide_payment"), "FilterPaymentMethods"],
[t(".form.tag_rules.show_hide_order_cycles"), "FilterOrderCycles"]
{ id: "FilterShippingMethods", name: t('js.tag_rules.show_hide_shipping') },
{ id: "FilterPaymentMethods", name: t('js.tag_rules.show_hide_payment') },
{ id: "FilterOrderCycles", name: t('js.tag_rules.show_hide_order_cycles') }
]
if helpers.feature?(:variant_tag, @object)
@tag_rule_types.prepend([t(".form.tag_rules.show_hide_variants"), "FilterVariants"])
elsif helpers.feature?(:inventory, @object)
@tag_rule_types.prepend([t(".form.tag_rules.show_hide_variants"), "FilterProducts"])
end
end
return unless helpers.feature?(:inventory, @object)
def load_tag_rules
if helpers.feature?(:variant_tag, @object)
@default_rules = @enterprise.tag_rules.exclude_inventory.select(&:is_default)
@rules = @enterprise.tag_rules.exclude_inventory.prioritised.reject(&:is_default)
elsif helpers.feature?(:inventory, @object)
@default_rules = @enterprise.tag_rules.exclude_variant.select(&:is_default)
@rules = @enterprise.tag_rules.exclude_variant.prioritised.reject(&:is_default)
else
@default_rules =
@enterprise.tag_rules.exclude_inventory.exclude_variant.select(&:is_default)
@rules =
@enterprise.tag_rules.exclude_inventory.exclude_variant.prioritised.reject(&:is_default)
end
@tag_rule_types.prepend({ id: "FilterProducts", name: t('js.tag_rules.show_hide_variants') })
end
def setup_property

View File

@@ -49,7 +49,7 @@ module Admin
errors: @importer.errors.full_messages
}
if helpers.inventory_enabled?(spree_current_user.enterprises)
if helpers.feature?(:inventory, spree_current_user.enterprises)
json[:results][:inventory_created] = @importer.inventory_created_count
json[:results][:inventory_updated] = @importer.inventory_updated_count
end
@@ -175,7 +175,7 @@ module Admin
# Return an error if trying to import into inventories when inventory is disable
def can_import_into_inventories?
return true if helpers.inventory_enabled?(spree_current_user.enterprises) ||
return true if helpers.feature?(:inventory, spree_current_user.enterprises) ||
params.dig(:settings, "import_into") != 'inventories'
redirect_to admin_product_import_url, notice: I18n.t(:product_import_inventory_disable)

View File

@@ -7,13 +7,11 @@ module Admin
before_action :init_filters_params
before_action :init_pagination_params
before_action :init_none_tag
def index
fetch_products
render "index",
locals: { producer_options:, categories:, tax_category_options:, available_tags:,
flash:, allowed_producers: }
locals: { producers:, categories:, tax_category_options:, available_tags:, flash: }
session[:products_return_to_url] = request.url
end
@@ -28,14 +26,13 @@ module Admin
flash[:success] = I18n.t('admin.products_v3.bulk_update.success')
redirect_to [:index,
{ page: @page, per_page: @per_page, search_term: @search_term,
producer_id: @producer_id, category_id: @category_id, tags_name_in: @tags }]
producer_id: @producer_id, category_id: @category_id }]
elsif product_set.errors.present?
@error_counts = { saved: product_set.saved_count, invalid: product_set.invalid.count }
render "index", status: :unprocessable_entity,
locals: {
producer_options:, categories:, tax_category_options:, available_tags:,
allowed_producers:, flash:
producers:, categories:, tax_category_options:, available_tags:, flash:
}
end
end
@@ -81,29 +78,27 @@ module Admin
end
def clone
product = Spree::Product.find(params[:id])
authorize! :clone, product
@product = Spree::Product.find(params[:id])
authorize! :clone, @product
status = :ok
begin
cloned_product = product.duplicate
@cloned_product = @product.duplicate
flash.now[:success] = t('.success')
product_index = "-#{cloned_product.id}"
@product_index = "-#{@cloned_product.id}"
@producer_options = producers
@category_options = categories
@tax_category_options = tax_category_options
rescue ActiveRecord::ActiveRecordError => e
flash.now[:error] = clone_error_message(e)
status = :unprocessable_entity
product_index = "-1" # Create a unique enough index
@product_index = "-1" # Create a unique enough index
end
respond_with do |format|
format.turbo_stream {
render :clone, status:,
locals: { product:, cloned_product:, product_index:, producer_options:,
category_options: categories, tax_category_options:,
allowed_producers: }
}
format.turbo_stream { render :clone, status: }
end
end
@@ -120,7 +115,7 @@ module Admin
@search_term = params[:search_term] || params[:_search_term]
@producer_id = params[:producer_id] || params[:_producer_id]
@category_id = params[:category_id] || params[:_category_id]
@tags = params[:tags_name_in] || []
@tags = params[:tags_name_in] || params[:_tags_name_in]
end
def init_pagination_params
@@ -128,32 +123,12 @@ module Admin
@page = params[:page].presence || 1
@per_page = params[:per_page].presence || 15
@q = params.permit(q: {})[:q] || { s: 'name asc' }
# Transform on_hand sorting to properly handle On-Demand products:
# - On-Demand products should ignore on_hand completely and sort alphabetically.
# - Non-On-Demand products should continue sorting by on_hand as usual.
if @q[:s] == 'on_hand asc'
@q[:s] = [
'backorderable_priority asc',
'backorderable_name asc',
@q[:s]
]
elsif @q[:s] == 'on_hand desc'
@q[:s] = [
'backorderable_priority desc',
'backorderable_name asc',
@q[:s]
]
end
end
def allowed_producers
OpenFoodNetwork::Permissions.new(spree_current_user)
def producers
producers = OpenFoodNetwork::Permissions.new(spree_current_user)
.managed_product_enterprises.is_primary_producer.by_name
end
def producer_options
allowed_producers.map { |p| [p.name, p.id] }
producers.map { |p| [p.name, p.id] }
end
def categories
@@ -180,30 +155,8 @@ module Admin
product_query = OpenFoodNetwork::Permissions.new(spree_current_user)
.editable_products.merge(product_scope_with_includes).ransack(ransack_query).result
product_query = apply_tags_filter(product_query)
# Postgres requires ORDER BY expressions to appear in the SELECT list when using DISTINCT.
# When the current ransack sort uses the computed stock columns, include them in the select
# so the generated COUNT/DISTINCT query is valid.
sort_columns = Array(@q && @q[:s]).flatten
if sort_columns.any? { |s|
s.to_s.include?('on_hand') || s.to_s.include?('backorderable_priority')
}
product_query = product_query.select(
Arel.sql('spree_products.*'),
Spree::Product.backorderable_priority_sql,
Spree::Product.backorderable_name_sql,
Spree::Product.on_hand_sql
)
end
@pagy, @products = pagy(
product_query.order(:name),
limit: @per_page,
page: @page,
size: [1, 2, 2, 1]
)
@pagy, @products = pagy(product_query.order(:name), limit: @per_page, page: @page,
size: [1, 2, 2, 1])
end
def product_scope
@@ -228,51 +181,12 @@ module Admin
query.merge!(Spree::Variant::SEARCH_KEY => @search_term)
end
query.merge!(variants_primary_taxon_id_in: @category_id) if @category_id.present?
query.merge!(variants_tags_name_in: @tags) if @tags.present?
query.merge!(@q) if @q
query
end
# Apply tags filter with OR logic:
# - Products with variants having selected tags
# - OR products with variants having no tags (when "None" is selected)
#
# Note: This cannot be implemented using Ransack because Ransack applies
# AND semantics across associations and cannot express OR logic that combines
# the presence and absence of the same associated records.
def apply_tags_filter(base_query)
return base_query if @tags.blank?
tag_names = Array(@tags).dup
has_none_tag = (tag_names.delete(@none_tag_value) == @none_tag_value)
queries = []
if tag_names.any?
# Products with at least one variant having one of the selected tags
tagged_product_ids = Spree::Variant
.joins(taggings: :tag)
.where(tags: { name: tag_names })
.select(:product_id)
queries << base_query.where(id: tagged_product_ids)
end
if has_none_tag
# Products where no variants have any tags
tagged_product_ids = Spree::Variant
.joins(:taggings)
.select(:product_id)
queries << base_query.where.not(id: tagged_product_ids)
end
return base_query if queries.empty?
# Combine queries using ActiveRecord's or method
queries.reduce { |combined, query| combined.or(query) }
end
# Optimise by pre-loading required columns
def product_query_includes
[
@@ -331,10 +245,6 @@ module Admin
t('.error')
end
end
def init_none_tag
@none_tag_value = '""'
end
end
end
# rubocop:enable Metrics/ClassLength

View File

@@ -4,8 +4,6 @@ module Admin
class ReportsController < Spree::Admin::BaseController
include ActiveStorage::SetCurrent
include ReportsActions
include Reports::AjaxSearch
helper ReportsHelper
before_action :authorize_report, only: [:show, :create]
@@ -24,12 +22,14 @@ module Admin
def show
@report = report_class.new(spree_current_user, params, render: false)
@rendering_options = rendering_options
show_report
end
def create
@report = report_class.new(spree_current_user, params, render: true)
update_rendering_options
render_in_background
end
@@ -61,9 +61,7 @@ module Admin
@blob = ReportBlob.create_for_upload_later!(report_filename)
ReportJob.perform_later(
report_class:,
user: spree_current_user,
params:,
report_class:, user: spree_current_user, params:,
format: report_format,
blob: @blob,
channel: ScopedChannel.for_id(params[:uuid]),

View File

@@ -61,7 +61,7 @@ module Admin
def destroy
if @object.destroy
flash[:success] = Spree.t(:successfully_removed)
flash[:success] = flash_message_for(@object, :successfully_removed)
respond_with(@object) do |format|
format.html { redirect_to collection_url }
format.js { render partial: "spree/admin/shared/destroy" }
@@ -76,7 +76,7 @@ module Admin
protected
def resource_not_found
flash[:error] = Spree.t(:not_found)
flash[:error] = flash_message_for(model_class.new, :not_found)
redirect_to collection_url
end

View File

@@ -6,7 +6,7 @@ module Admin
class StripeAccountsController < Spree::Admin::BaseController
def connect
payload = params.permit(:enterprise_id).to_h
key = Rails.application.secret_key_base
key = Openfoodnetwork::Application.config.secret_token
url_params = { state: JWT.encode(payload, key, 'HS256'), scope: "read_write" }
redirect_to Stripe::OAuth.authorize_url(url_params)
end

View File

@@ -1,47 +1,13 @@
# frozen_string_literal: true
module Admin
class TagRulesController < Spree::Admin::BaseController
class TagRulesController < Admin::ResourceController
respond_to :json
def new
@index = params[:index]
@div_id = params[:div_id]
is_default = params[:is_default]
@customer_tags = params[:customer_tags]
respond_override destroy: { json: {
success: lambda { head :no_content }
} }
status = :ok
if permitted_tag_rule_type.include?(params[:rule_type])
@default_rule = "TagRule::#{params[:rule_type]}".constantize.new(is_default:)
else
flash.now[:error] = t(".not_supported_type")
status = :internal_server_error
end
respond_with do |format|
format.turbo_stream { render :new, status: }
end
end
def destroy
@rule = TagRule.find(params[:id])
@index = params[:index]
authorize! :destroy, @rule
status = :ok
if @rule.destroy
flash[:success] = Spree.t(:successfully_removed, resource: Spree.t(:tag_rule))
else
flash.now[:error] = t(".destroy_error")
status = :internal_server_error
end
respond_to do |format|
format.turbo_stream { render :destroy, status: }
end
end
# Used by the tag input autocomplete
def map_by_tag
respond_to do |format|
format.json do
@@ -51,26 +17,6 @@ module Admin
end
end
# Use to populate autocomplete with available rule for the given tag/enterprise
def variant_tag_rules
tag_rules =
TagRule.matching_variant_tag_rules_by_enterprises(params[:enterprise_id], params[:q])
@formatted_tag_rules = tag_rules.each_with_object({}) do |rule, mapping|
rule.preferred_variant_tags.split(",").each do |tag|
if mapping[tag]
mapping[tag][:rules] += 1
else
mapping[tag] = { tag:, rules: 1 }
end
end
end.values
respond_with do |format|
format.html { render :variant_tag_rules, layout: false }
end
end
private
def collection_actions
@@ -93,13 +39,5 @@ module Admin
Enterprise.managed_by(spree_current_user)
end
end
def model_class
TagRule
end
def permitted_tag_rule_type
%w{FilterOrderCycles FilterPaymentMethods FilterProducts FilterShippingMethods FilterVariants}
end
end
end

View File

@@ -44,8 +44,6 @@ module Admin
def load_data
@hubs = OpenFoodNetwork::Permissions.new(spree_current_user).
variant_override_hubs.by_name
# Only display the ones with inventory enabled
@hubs = @hubs.select { |p| helpers.feature?(:inventory, p) }
# Used in JS to look up the name of the producer of each product
@producers = OpenFoodNetwork::Permissions.new(spree_current_user).

View File

@@ -14,7 +14,7 @@ module Admin
)
if @voucher.save
flash[:success] = I18n.t(:successfully_created, resource: Spree.t(:voucher))
flash[:success] = I18n.t(:successfully_created, resource: "Voucher")
redirect_to edit_admin_enterprise_path(@enterprise, anchor: :vouchers_panel)
else
render_error

View File

@@ -43,7 +43,7 @@ module Api
@enterprise = Enterprise.find_by(permalink: params[:id]) || Enterprise.find(params[:id])
authorize! :update, @enterprise
if params[:logo] && @enterprise.update!(logo: params[:logo])
if params[:logo] && @enterprise.update( logo: params[:logo] )
render(html: @enterprise.logo_url(:medium), status: :ok)
elsif params[:promo] && @enterprise.update!( promo_image: params[:promo] )
render(html: @enterprise.promo_image_url(:medium), status: :ok)

View File

@@ -7,7 +7,6 @@ module Api
module V0
class ExchangeProductsController < Api::V0::BaseController
include PaginationData
DEFAULT_PER_PAGE = 100
skip_authorization_check only: [:index]

View File

@@ -23,8 +23,7 @@ module Api
order_cycle,
customer,
search_params,
inventory_enabled:,
variant_tag_enabled:
inventory_enabled:
).products_json
render plain: products
@@ -97,17 +96,13 @@ module Api
def distributed_products
OrderCycles::DistributedProductsService.new(
distributor, order_cycle, customer, inventory_enabled:, variant_tag_enabled:,
distributor, order_cycle, customer, inventory_enabled:
).products_relation.pluck(:id)
end
def inventory_enabled
OpenFoodNetwork::FeatureToggle.enabled?(:inventory, distributor)
end
def variant_tag_enabled
OpenFoodNetwork::FeatureToggle.enabled?(:variant_tag, distributor)
end
end
end
end

View File

@@ -7,7 +7,6 @@ module Api
module V0
class ProductsController < Api::V0::BaseController
include PaginationData
respond_to :json
DEFAULT_PER_PAGE = 15

View File

@@ -8,10 +8,6 @@ module Api
include AddressTransformation
include ExtraFields
wrap_parameters :customer, include:
Customer.attribute_names +
[:billing_address, :shipping_address]
skip_authorization_check only: :index
before_action :authorize_action, only: [:show, :update, :destroy]
@@ -92,8 +88,7 @@ module Api
attributes = params.require(:customer).permit(
:email, :enterprise_id,
:code, :first_name, :last_name,
:billing_address,
shipping_address: [
:billing_address, shipping_address: [
:phone, :latitude, :longitude,
:first_name, :last_name,
:street_address_1, :street_address_2,

View File

@@ -4,7 +4,7 @@ module ManagerInvitations
extend ActiveSupport::Concern
def create_new_manager(email, enterprise)
password = SecureRandom.base58(64)
password = Devise.friendly_token
new_user = Spree::User.create(email:, unconfirmed_email: email, password:)
new_user.reset_password_token = Devise.friendly_token
# Same time as used in Devise's lib/devise/models/recoverable.rb.

Some files were not shown because too many files have changed in this diff Show More