Compare commits

...

62 Commits

Author SHA1 Message Date
dependabot[bot]
820ca10556 Bump bigdecimal from 3.3.1 to 4.1.0
Bumps [bigdecimal](https://github.com/ruby/bigdecimal) from 3.3.1 to 4.1.0.
- [Release notes](https://github.com/ruby/bigdecimal/releases)
- [Changelog](https://github.com/ruby/bigdecimal/blob/master/CHANGES.md)
- [Commits](https://github.com/ruby/bigdecimal/compare/v3.3.1...v4.1.0)

---
updated-dependencies:
- dependency-name: bigdecimal
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-07 09:57:07 +00:00
Maikel Linke
e10efe425a Update checksums 2026-04-07 13:25:49 +10:00
David Cook
12c36bf83e Merge pull request #14127 from mkllnk/bundler-checksums
Add checksums to Gemfile.lock to guard against gem replacement
2026-04-07 12:03:13 +10:00
Gaetan Craig-Riou
0f10ffbb66 Merge pull request #14131 from openfoodfoundation/dependabot/npm_and_yarn/lodash-4.18.1
Bump lodash from 4.17.23 to 4.18.1
2026-04-07 11:58:48 +10:00
Gaetan Craig-Riou
366c457474 Merge pull request #14133 from openfoodfoundation/dependabot/npm_and_yarn/mini-css-extract-plugin-2.10.2
Bump mini-css-extract-plugin from 2.10.1 to 2.10.2
2026-04-07 11:00:10 +10:00
Gaetan Craig-Riou
af8ea31c76 Merge pull request #14130 from openfoodfoundation/dependabot/bundler/flipper-1.4.1
Bump flipper from 1.4.0 to 1.4.1
2026-04-07 10:55:34 +10:00
Ahmed Ejaz
d1608a0288 Update all locales with the latest Transifex translations 2026-04-04 21:46:56 +05:00
Ahmed Ejaz
e4e7ef395b Merge pull request #14113 from gbathree/13817-fix-guest-order-cancellation
Fix guest order cancellation redirecting to home page
2026-04-04 21:42:44 +05:00
Ahmed Ejaz
ec24740c3b Merge pull request #14085 from dacook/admin-product-actions-fixes
[Admin Products] Action menu fixes
2026-04-04 21:42:12 +05:00
dependabot[bot]
4e0f5225b4 Bump mini-css-extract-plugin from 2.10.1 to 2.10.2
Bumps [mini-css-extract-plugin](https://github.com/webpack/mini-css-extract-plugin) from 2.10.1 to 2.10.2.
- [Release notes](https://github.com/webpack/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack/mini-css-extract-plugin/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/mini-css-extract-plugin/compare/v2.10.1...v2.10.2)

---
updated-dependencies:
- dependency-name: mini-css-extract-plugin
  dependency-version: 2.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-03 09:32:58 +00:00
Gaetan Craig-Riou
783ac990bc Merge pull request #14132 from openfoodfoundation/dependabot/bundler/rack-2.2.23
Bump rack from 2.2.22 to 2.2.23
2026-04-03 09:33:02 +11:00
dependabot[bot]
a4cc2f17dc Bump rack from 2.2.22 to 2.2.23
Bumps [rack](https://github.com/rack/rack) from 2.2.22 to 2.2.23.
- [Release notes](https://github.com/rack/rack/releases)
- [Changelog](https://github.com/rack/rack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rack/rack/compare/v2.2.22...v2.2.23)

---
updated-dependencies:
- dependency-name: rack
  dependency-version: 2.2.23
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-02 20:02:57 +00:00
Rachel Arnould
c19241ddd9 Merge pull request #14118 from dacook/linked-variants-14088
Prevent creating a linked variant from a linked variant
2026-04-02 17:44:34 +02:00
dependabot[bot]
92423106ef Bump lodash from 4.17.23 to 4.18.1
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-02 03:25:10 +00:00
dependabot[bot]
6b876a0051 Bump flipper from 1.4.0 to 1.4.1
Bumps [flipper](https://github.com/flippercloud/flipper) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/flippercloud/flipper/releases)
- [Changelog](https://github.com/flippercloud/flipper/blob/main/Changelog.md)
- [Commits](https://github.com/flippercloud/flipper/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: flipper
  dependency-version: 1.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-02 01:00:16 +00:00
Maikel
eee9f61c38 Merge pull request #14129 from openfoodfoundation/dependabot/bundler/aws-sdk-s3-1.217.0
Bump aws-sdk-s3 from 1.215.0 to 1.217.0
2026-04-02 11:50:42 +11:00
Maikel
7f711d746f Merge pull request #14126 from dacook/dependabot-cooldown
Dependabot cooldown
2026-04-02 11:49:55 +11:00
dependabot[bot]
732234f1c0 Bump aws-sdk-s3 from 1.215.0 to 1.217.0
Bumps [aws-sdk-s3](https://github.com/aws/aws-sdk-ruby) from 1.215.0 to 1.217.0.
- [Release notes](https://github.com/aws/aws-sdk-ruby/releases)
- [Changelog](https://github.com/aws/aws-sdk-ruby/blob/version-3/gems/aws-sdk-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-ruby/commits)

---
updated-dependencies:
- dependency-name: aws-sdk-s3
  dependency-version: 1.217.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-01 11:11:34 +00:00
Maikel
450fe4ada1 Merge pull request #14119 from mkllnk/replace-whenever
Replace whenever with sidekiq scheduler
2026-04-01 15:37:33 +11:00
Maikel Linke
bcecbf9a0f Require rake dependency to run it within jobs 2026-04-01 15:18:49 +11:00
David Cook
15abea51ab Avoid unnecessary extra page visit
The second spec example below has to load the page after creating a record, so it's not helpful to load it here.
2026-04-01 14:48:18 +11:00
David Cook
cacb62f58c Style fix 2026-04-01 14:11:19 +11:00
David Cook
6048fcb053 Define function as member arrow function
This way it behaves as an instance method, and we don't have to pass in the object.
2026-04-01 14:11:19 +11:00
David Cook
6013b6be70 Remove transform at end of animation
During transform, any overflow on the element is clipped/hidden. This caused all dropdown menus to be clipped and unusable. Now, once the animation is complete, the overflow is visible, and menus are usable.

Mistral Vibe AI was used to find this solution. I tried to find a CSS solution last week but failed, then started to consider using JS to remove the class, but decided against it once I realised that the product clone JS was already doing that, and it didn't seem to solve the clipping issue.
So I asked Mistral Vibe and it suggested adding 'forwards' (before it had spent energy on evaluating the existing style rules). As you can see 'forwards' was already there, but removing it helped. So Mistral was wrong, but at least pointed me in the right direction, yay!
2026-04-01 14:11:19 +11:00
David Cook
22a1528ac7 Show unit in tooltip
Variants may have the same name, or no display_name at all. This helper method provides a more comprehensive way of describing the variant.
2026-04-01 14:11:19 +11:00
David Cook
ca3c0c98bf Don't group reviewdog output
Grouping is a nice feature, but it wasn't helpful here. If there's an error in rubocop for example, the rubocop section will be collapsed, and because we didn't close the group, the haml group was always open. So it wasn't clear where the error was.

Better to just show all the output, which isn't very long, so you can see where the problem is straight away.

Even better would be to add support for GitHub Actions annotations. I thought we used to have that turned on, not sure why it's not working now.
2026-04-01 14:11:19 +11:00
David Cook
19006d6c17 Close action menu when making a selection
But don't hide it immediately, because the user can't see if they made a selection, or accidentally closed it. Instead, fade slowly so that you can see the selected option momentarily (like system menus). This gives enough feedback while we wait for the selected action to perform.

I did attempt a blink on the item background colour, like my favourite OS does which is really helpful. But couldn't get the CSS to work.
2026-04-01 14:11:19 +11:00
David Cook
da69e2c383 Widen action menus slightly when needed 2026-04-01 14:11:19 +11:00
Maikel Linke
2fbfe590d7 Add checksums to Gemfile.lock to guard against gem replacement
Result of:

```
bundle lock --add-checksums
```

Recommendation from:

- https://www.fastruby.io/blog/hidden-dangers-in-your-gemfile.html
2026-04-01 13:51:06 +11:00
Maikel
2e6e4b665f Merge pull request #14122 from openfoodfoundation/dependabot/bundler/view_component-4.5.0
Bump view_component from 4.1.1 to 4.5.0
2026-04-01 10:31:41 +11:00
David Cook
e255bcc082 Formatting
Compacted and adjusted comments to make it a bit easier to read.
2026-04-01 10:31:34 +11:00
David Cook
51b4dc64cc Add cooldown for turbo_power
Ensure it's treated the same as other gems and packages.
2026-04-01 10:17:40 +11:00
Maikel
77b6bc15e7 Merge pull request #14121 from openfoodfoundation/dependabot/bundler/devise-i18n-1.16.0
Bump devise-i18n from 1.15.0 to 1.16.0
2026-04-01 10:11:37 +11:00
Ahmed Ejaz
9b145da898 Merge pull request #14040 from chahmedejaz/task/13797-improve-performance-of-products-page
Fix Admin Bulk Products screen performance issue
2026-04-01 00:37:40 +05:00
dependabot[bot]
00d600911d Bump view_component from 4.1.1 to 4.5.0
Bumps [view_component](https://github.com/viewcomponent/view_component) from 4.1.1 to 4.5.0.
- [Release notes](https://github.com/viewcomponent/view_component/releases)
- [Changelog](https://github.com/ViewComponent/view_component/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/viewcomponent/view_component/compare/v4.1.1...v4.5.0)

---
updated-dependencies:
- dependency-name: view_component
  dependency-version: 4.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-31 09:55:00 +00:00
dependabot[bot]
10d6dd73f2 Bump devise-i18n from 1.15.0 to 1.16.0
Bumps [devise-i18n](https://github.com/devise-i18n/devise-i18n) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/devise-i18n/devise-i18n/releases)
- [Changelog](https://github.com/devise-i18n/devise-i18n/blob/main/CHANGELOG.md)
- [Commits](https://github.com/devise-i18n/devise-i18n/compare/v1.15.0...v1.16.0)

---
updated-dependencies:
- dependency-name: devise-i18n
  dependency-version: 1.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-31 09:41:05 +00:00
Maikel Linke
c74624cd57 Remove unused gem whenever 2026-03-31 14:58:57 +11:00
Maikel Linke
60edcada2c Remove whenever config 2026-03-31 14:56:41 +11:00
Maikel Linke
b61f6ab444 Schedule all jobs with Sidekiq 2026-03-31 14:53:26 +11:00
David Cook
ccc38367f3 Prevent creating a linked variant from a linked variant
It's just too confusing.
2026-03-31 14:34:17 +11:00
Maikel Linke
80a12db191 Move database clean from cron to Sidekiq scheduler
After moving the remaining tasks from schedule.rb to sidekiq.yml, we can
remove whenever and won't rely on cron any more. That will simplify the
setup and migration to a new server.
2026-03-31 12:34:47 +11:00
Maikel
5beed6f028 Merge pull request #14117 from openfoodfoundation/dependabot/bundler/whenever-1.1.2
Bump whenever from 1.1.0 to 1.1.2
2026-03-31 10:27:56 +11:00
Ahmed Ejaz
0a65322594 rename ajax_search_spec 2026-03-31 04:05:06 +05:00
Ahmed Ejaz
b7f154d289 revert back the bin/setup 2026-03-31 03:49:35 +05:00
Maikel
edb8a03436 Merge pull request #14116 from openfoodfoundation/dependabot/bundler/active_storage_validations-3.0.4
Bump active_storage_validations from 3.0.3 to 3.0.4
2026-03-31 09:35:28 +11:00
Ahmed Ejaz
3ee338fa8d Add ajax search controller 2026-03-31 01:54:02 +05:00
dependabot[bot]
e3da27ca12 Bump whenever from 1.1.0 to 1.1.2
Bumps [whenever](https://github.com/javan/whenever) from 1.1.0 to 1.1.2.
- [Release notes](https://github.com/javan/whenever/releases)
- [Changelog](https://github.com/javan/whenever/blob/main/CHANGELOG.md)
- [Commits](https://github.com/javan/whenever/compare/v1.1.0...v1.1.2)

---
updated-dependencies:
- dependency-name: whenever
  dependency-version: 1.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 10:17:45 +00:00
dependabot[bot]
4c8e6d8260 Bump active_storage_validations from 3.0.3 to 3.0.4
Bumps [active_storage_validations](https://github.com/igorkasyanchuk/active_storage_validations) from 3.0.3 to 3.0.4.
- [Release notes](https://github.com/igorkasyanchuk/active_storage_validations/releases)
- [Changelog](https://github.com/igorkasyanchuk/active_storage_validations/blob/master/CHANGES.md)
- [Commits](https://github.com/igorkasyanchuk/active_storage_validations/compare/3.0.03...3.0.4)

---
updated-dependencies:
- dependency-name: active_storage_validations
  dependency-version: 3.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 10:16:06 +00:00
Maikel
de28083007 Merge pull request #14112 from openfoodfoundation/dependabot/bundler/bootsnap-1.23.0
Bump bootsnap from 1.22.0 to 1.23.0
2026-03-30 11:59:05 +11:00
Greg Austic
cc608adddc Address review feedback: use referer in specs, remove demo screenshots
- Set HTTP_REFERER in cancel action specs so they test the redirect to
  the actual order page (not the cancel path, which was the implicit
  referer in controller specs)
- Keep response.body match pattern (consistent with checkout_controller_spec
  for CableReady redirects — redirect_to matcher does not work here since
  the cancel action uses cable_car.redirect_to, not a Rails redirect)
- Remove doc/demo screenshots; images to be added to PR description instead

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 19:03:47 -04:00
Gaetan Craig-Riou
01bfd72387 Merge pull request #14115 from openfoodfoundation/dependabot/npm_and_yarn/trix-2.1.18
Bump trix from 2.1.17 to 2.1.18
2026-03-30 09:57:33 +11:00
Gaetan Craig-Riou
69d9c52a53 Merge pull request #14111 from openfoodfoundation/dependabot/bundler/pg-1.6.3
Bump pg from 1.6.2 to 1.6.3
2026-03-30 09:44:59 +11:00
dependabot[bot]
5371361a74 Bump trix from 2.1.17 to 2.1.18
Bumps [trix](https://github.com/basecamp/trix) from 2.1.17 to 2.1.18.
- [Release notes](https://github.com/basecamp/trix/releases)
- [Commits](https://github.com/basecamp/trix/compare/v2.1.17...v2.1.18)

---
updated-dependencies:
- dependency-name: trix
  dependency-version: 2.1.18
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-29 18:49:52 +00:00
Greg Austic
a6f5f2c10d Add demo screenshots for guest order cancellation fix
Before/after screenshots showing the fix works end-to-end:
- step1: guest order confirmed, Cancel Order button visible
- step2: same page after cancellation, order shows Cancelled

These can be removed after the PR is reviewed and merged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 09:44:12 -04:00
Greg Austic
c72976b1e2 Fix guest order cancellation redirecting to home page
When a guest places an order and tries to cancel it from the order
confirmation page, the cancellation silently failed and redirected
to the home page. The guest was left unsure whether the order was
cancelled, and the hub received no cancellation notification.

Root cause: two missing pieces for guest (token-based) authorization:

1. The `:cancel` ability in Ability#add_shopping_abilities only checked
   `order.user == user`, ignoring the guest token. The `:read` and
   `:update` abilities already support `order.token && token == order.token`
   as a fallback — `:cancel` now does the same.

2. The `cancel` action called `authorize! :cancel, @order` without
   passing `session[:access_token]`, so even with the corrected ability
   the token was never evaluated.

Fixes #13817

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 09:05:47 -04:00
dependabot[bot]
b7c628dc2a Bump bootsnap from 1.22.0 to 1.23.0
Bumps [bootsnap](https://github.com/rails/bootsnap) from 1.22.0 to 1.23.0.
- [Release notes](https://github.com/rails/bootsnap/releases)
- [Changelog](https://github.com/rails/bootsnap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rails/bootsnap/compare/v1.22.0...v1.23.0)

---
updated-dependencies:
- dependency-name: bootsnap
  dependency-version: 1.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-27 09:34:18 +00:00
dependabot[bot]
5bef61aa2e Bump pg from 1.6.2 to 1.6.3
Bumps [pg](https://github.com/ged/ruby-pg) from 1.6.2 to 1.6.3.
- [Changelog](https://github.com/ged/ruby-pg/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ged/ruby-pg/compare/v1.6.2...v1.6.3)

---
updated-dependencies:
- dependency-name: pg
  dependency-version: 1.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-27 09:33:08 +00:00
Maikel
79c346acb1 Merge pull request #14109 from openfoodfoundation/dependabot/npm_and_yarn/node-forge-1.4.0
Bump node-forge from 1.3.3 to 1.4.0
2026-03-27 13:56:43 +11:00
dependabot[bot]
ca10ae2f5c Bump node-forge from 1.3.3 to 1.4.0
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.3.3 to 1.4.0.
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.3.3...v1.4.0)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-version: 1.4.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-27 00:12:35 +00:00
Ahmed Ejaz
8ba0ab6b5a Update specs according to new remote search function on products page 2026-03-25 02:01:36 +05:00
Ahmed Ejaz
044f6131da fix aria_label translations 2026-03-25 01:30:06 +05:00
Ahmed Ejaz
062fcd317c Add searchable dropdowns for producers, categories, and tax categories in products_v3 2026-03-25 01:30:06 +05:00
35 changed files with 1058 additions and 226 deletions

6
.env
View File

@@ -21,12 +21,6 @@ CHECKOUT_ZONE="Australia"
# Find currency codes at http://en.wikipedia.org/wiki/ISO_4217.
CURRENCY="AUD"
# The whenever gem can set the `MAILTO` variable for our cron jobs.
# You can define an email address to notify if any job outputs something.
# But you need a working mail server setup so that the message is delivered.
# See: config/schedule.rb
# SCHEDULE_NOTIFICATIONS="admin@example.com"
# Mail settings
MAIL_HOST="example.com"
MAIL_DOMAIN="example.com"

View File

@@ -9,32 +9,30 @@ multi-ecosystem-groups:
turbo_power:
schedule:
interval: "daily"
cooldown:
default-days: 7
updates:
# turbo_power: ensure gem and package are updated together
- 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
# All others
- package-ecosystem: "bundler"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 7
# Only specific requirements are specified in Gemfile, so don't touch it.
# Only specific requirements are specified in Gemfile, so don't let Dependabot touch it.
versioning-strategy: lockfile-only
- package-ecosystem: "npm"
@@ -43,6 +41,5 @@ updates:
interval: "daily"
cooldown:
default-days: 7
# Only specific requirements are specified in package.json, so don't touch it.
# Only specific requirements are specified in package.json, so don't let Dependabot touch it.
versioning-strategy: lockfile-only

View File

@@ -118,8 +118,6 @@ gem 'immigrant'
gem 'roo' # read spreadsheets
gem 'spreadsheet_architect' # write spreadsheets
gem 'whenever', require: false
gem 'coffee-rails', '~> 5.0.0'
gem 'angular_rails_csrf'

View File

@@ -112,7 +112,7 @@ GEM
rails-html-sanitizer (~> 1.6)
active_model_serializers (0.8.4)
activemodel (>= 3.0)
active_storage_validations (3.0.3)
active_storage_validations (3.0.4)
activejob (>= 6.1.4)
activemodel (>= 6.1.4)
activestorage (>= 6.1.4)
@@ -185,8 +185,8 @@ GEM
ast (2.4.3)
attr_required (1.0.2)
aws-eventstream (1.4.0)
aws-partitions (1.1227.0)
aws-sdk-core (3.243.0)
aws-partitions (1.1233.0)
aws-sdk-core (3.244.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
@@ -194,11 +194,11 @@ GEM
bigdecimal
jmespath (~> 1, >= 1.6.1)
logger
aws-sdk-kms (1.122.0)
aws-sdk-core (~> 3, >= 3.241.4)
aws-sdk-kms (1.123.0)
aws-sdk-core (~> 3, >= 3.244.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.215.0)
aws-sdk-core (~> 3, >= 3.243.0)
aws-sdk-s3 (1.217.0)
aws-sdk-core (~> 3, >= 3.244.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.12.1)
@@ -207,10 +207,10 @@ GEM
bcp47_spec (0.2.1)
bcrypt (3.1.22)
benchmark (0.5.0)
bigdecimal (3.3.1)
bigdecimal (4.1.0)
bindata (2.5.1)
bindex (0.8.1)
bootsnap (1.22.0)
bootsnap (1.23.0)
msgpack (~> 1.2)
bugsnag (6.29.0)
concurrent-ruby (~> 1.0)
@@ -245,7 +245,6 @@ GEM
cgi (0.5.1)
childprocess (5.0.0)
choice (0.2.0)
chronic (0.10.2)
coderay (1.1.3)
coffee-rails (5.0.0)
coffee-script (>= 2.2.0)
@@ -290,8 +289,8 @@ GEM
warden (~> 1.2.3)
devise-encryptable (0.2.0)
devise (>= 2.1.0)
devise-i18n (1.15.0)
devise (>= 4.9.0)
devise-i18n (1.16.0)
devise (>= 5.0.0)
rails-i18n
diff-lcs (1.6.2)
digest (3.2.1)
@@ -340,7 +339,7 @@ GEM
websocket-driver (~> 0.7)
ffaker (2.25.0)
ffi (1.17.3)
flipper (1.4.0)
flipper (1.4.1)
concurrent-ruby (< 2)
flipper-active_record (1.4.0)
activerecord (>= 4.2, < 9)
@@ -591,7 +590,7 @@ GEM
hashery (~> 2.0)
ruby-rc4
ttfunk
pg (1.6.2)
pg (1.6.3)
pp (0.6.3)
prettyprint
prettyprint (0.2.0)
@@ -621,7 +620,7 @@ GEM
railties (>= 4.2)
raabro (1.4.0)
racc (1.8.1)
rack (2.2.22)
rack (2.2.23)
rack-mini-profiler (2.3.4)
rack (>= 1.2.0)
rack-oauth2 (2.3.0)
@@ -697,19 +696,13 @@ GEM
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rdf (3.3.4)
rdf (3.3.1)
bcp47_spec (~> 0.2)
bigdecimal (~> 3.1, >= 3.1.5)
link_header (~> 0.0, >= 0.0.8)
logger (~> 1.5)
ostruct (~> 0.6)
readline (~> 0.0)
rdoc (7.2.0)
erb
psych (>= 4.0.0)
tsort
readline (0.0.4)
reline
redcarpet (3.6.1)
redis (5.4.1)
redis-client (>= 0.22.0)
@@ -905,10 +898,9 @@ GEM
thor (1.5.0)
thread-local (1.1.0)
tilt (2.7.0)
timeout (0.6.0)
timeout (0.6.1)
tsort (0.2.0)
ttfunk (1.8.0)
bigdecimal (~> 3.1)
ttfunk (1.7.0)
turbo-rails (2.0.23)
actionpack (>= 7.1.0)
railties (>= 7.1.0)
@@ -938,9 +930,9 @@ GEM
validates_lengths_from_database (0.8.0)
activerecord (>= 4)
vcr (6.4.0)
view_component (4.1.1)
actionview (>= 7.1.0, < 8.2)
activesupport (>= 7.1.0, < 8.2)
view_component (4.5.0)
actionview (>= 7.1.0)
activesupport (>= 7.1.0)
concurrent-ruby (~> 1)
view_component_reflex (3.1.14.pre9)
rails (>= 5.2, < 8.0)
@@ -968,8 +960,6 @@ GEM
base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
whenever (1.1.0)
chronic (>= 0.6.3)
xml-simple (1.1.8)
xpath (3.2.0)
nokogiri (~> 1.8)
@@ -1127,10 +1117,354 @@ DEPENDENCIES
web!
web-console
webmock
whenever
wicked_pdf!
wkhtmltopdf-binary!
CHECKSUMS
Ascii85 (2.0.1) sha256=15cb5d941808543cbb9e7e6aea3c8ec3877f154c3461e8b3673e97f7ecedbe5a
actioncable (7.1.6) sha256=ad428d5f0a810452160820ae3cf3d9d68d8f59e7c76de3bd1f1de2a5ad03c3da
actionmailbox (7.1.6) sha256=ded958ad8ec147a5f14555833541f07063af188777b09b50cfeeaa623bc2f731
actionmailer (7.1.6) sha256=b07f6420ec66bd299a9da5a35c075849fbd5504e82793301b0c275fa4211d273
actionpack (7.1.6) sha256=3fa42da36fdcfc3690a711ed35ac5d527b87d3d676f8d111238aa399151203eb
actionpack-action_caching (1.2.2) sha256=2da245520de40dda6b9bed2151dfed1e68644cf22ca23239966d62d834faaaaf
actiontext (7.1.6) sha256=79d657422dd67cc8cb46866a7bec9d89ec8699f7fa5647c0eab3472dc0297e66
actionview (7.1.6) sha256=11147d81f90465ae062b2a77805c6f8f446e044e309c51bd9449bdbd43edf566
active_model_serializers (0.8.4) sha256=7350e3d3b6a5946bbec033241d908013cc85ff4d584230e6aa074225547c754c
active_storage_validations (3.0.4) sha256=134ba51d5166cd419311fe1510d7eb64af6996e7041d8d56c26a2f151abb6522
activejob (7.1.6) sha256=0dd9cd051d494608349dd9223a3e61c3933250db77e35ab6617c26c1d52dccbb
activemerchant (1.137.0) sha256=fdf5e33f5c94d1981bd632d72e282c0a5afaf06bac6df3f933c08a95e98bd5ce
activemodel (7.1.6) sha256=f72f510018a560b5969e3ffc88214441ff09eed60b310feba678a597b2a2e721
activerecord (7.1.6) sha256=1aa298cd7fc97ed8639ebb05a46bd17243a1218d89945bdc2bac1e61e673f079
activerecord-import (2.2.0) sha256=f8ca99b196e50775723d1f1d192c379f656378dc9f5628240992a0d78807fa4b
activerecord-postgresql-adapter (0.0.1) sha256=b42b23d8e94f32b32e9a5f5cb4789735140393e928546b328d48683b6b68aef1
activerecord-session_store (2.2.0) sha256=65918054573683bf4f87af89e765e1fece14c9d71cfac1f11abe4687c96e2743
activestorage (7.1.6) sha256=2f1acb8e6592ba783d9cbc3da93ac4477d441dffc5d533ceccbbfab39f4bf398
activesupport (7.1.6) sha256=7f12140a813b1c4922a322663e547129aef1840fc512fa262378f6d7e7fd3a7c
acts-as-taggable-on (13.0.0) sha256=dca776c6ddebc458d175b57554ad692488e283f27047117e01ed871f1cba6135
acts_as_list (1.0.4) sha256=5516558f4058b369191e3cb9af0428d1c75f8e98f886b218b3ad10960a14e28a
addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485
aes_key_wrap (1.1.0) sha256=b935f4756b37375895db45669e79dfcdc0f7901e12d4e08974d5540c8e0776a5
afm (1.0.0) sha256=5bd4d6f6241e7014ef090985ec6f4c3e9745f6de0828ddd58bc1efdd138f4545
angular-rails-templates (1.4.0) sha256=1eaf00a796eaeca9574acdc1adc3c4f005d004c3d938003520fa19853236398d
angular_rails_csrf (7.0.2) sha256=b9672752efa7ec1089c864e3bd130b44bd7711471380f3dc179b03d368d31e7f
angularjs-file-upload-rails (2.4.1) sha256=6de1d8a70b0b6ccd5656786bc42d2e949d70d1c126dc6c02d30addc6175a401f
angularjs-rails (1.8.0) sha256=fde5805a84dd760e7af29ceb528391adf7e9ed63f24857b43954a0f7b3847405
arel-helpers (2.17.0) sha256=0af23b8d32c3ef670eda04e6e371ab177ed684f0edaa06aeed57f822bc0acf6d
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
attr_required (1.0.2) sha256=f0ebfc56b35e874f4d0ae799066dbc1f81efefe2364ca3803dc9ea6a4de6cb99
aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b
aws-partitions (1.1233.0) sha256=928d3486082db11659397eb4c957f41e33fac8848bf87eb42fd921bbb96213c2
aws-sdk-core (3.244.0) sha256=3e458c078b0c5bdee95bc370c3a483374b3224cf730c1f9f0faf849a5d9a18ea
aws-sdk-kms (1.123.0) sha256=d405f37e82f8fa32045ca8980be266c0b45b37aaf2012afe0254321a1e811f20
aws-sdk-s3 (1.217.0) sha256=6ea709272c666888b14e9c62345abd9a6a967759ae13667c28f01fde6823c24b
aws-sigv4 (1.12.1) sha256=6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
bcp47_spec (0.2.1) sha256=3fd62edf96c126bd9624e4319ac74082a966081859d1ee0ef3c3041640a37810
bcrypt (3.1.22) sha256=1f0072e88c2d705d94aff7f2c5cb02eb3f1ec4b8368671e19112527489f29032
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
bigdecimal (4.1.0) sha256=6dc07767aa3dc456ccd48e7ae70a07b474e9afd7c5bc576f80bd6da5c8dd6cae
bindata (2.5.1) sha256=53186a1ec2da943d4cb413583d680644eb810aacbf8902497aac8f191fad9e58
bindex (0.8.1) sha256=7b1ecc9dc539ed8bccfc8cb4d2732046227b09d6f37582ff12e50a5047ceb17e
bootsnap (1.23.0) sha256=c1254f458d58558b58be0f8eb8f6eec2821456785b7cdd1e16248e2020d3f214
bugsnag (6.29.0) sha256=35ebdcfa2e12f1147ed353dbb7bdfa97e93d363afde2fcd7d27c3b5410d9a9e7
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
bullet (8.1.0) sha256=604b7e2636ec2137dcab3ba61a56248c39a0004a0c9405d58bad0686d23b98ff
cable_ready (5.0.6) sha256=de1c94beab04b15a47c8794507a0b5d0ce9c0f25edb1b60a7542adc5413ea894
cancancan (1.15.0) sha256=05f540c62143fde51f7ceaba8584f8dc0e34c93e43ab7a6c1a832b4553216b16
capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef
capybara-shadowdom (0.3.0) sha256=956b57c90c91c48fd45797d806cbbb40b353ad247e8fb68c125c9d579529bfa5
catalog (0.0.1)
caxlsx (4.0.0) sha256=a93a1c23a076caed4ddc4e944d66d57439b817c58e44f95df92e4db982524cc9
cgi (0.5.1) sha256=e93fcafc69b8a934fe1e6146121fa35430efa8b4a4047c4893764067036f18e9
childprocess (5.0.0) sha256=0746b7ab1d6c68156e64a3767631d7124121516192c0492929a7f0af7310d835
choice (0.2.0) sha256=a19617f7dfd4921b38a85d0616446620de685a113ec6d1ecc85bdb67bf38c974
coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b
coffee-rails (5.0.0) sha256=5daaa1ba51fd4907c98a333b6a9e7c1a99b1fff57fcef999b6c62d813cb91a9c
coffee-script (2.4.1) sha256=82fe281e11b93c8117b98c5ea8063e71741870f1c4fbb27177d7d6333dd38765
coffee-script-source (1.12.2) sha256=e12b16fd8927fbbf8b87cb2e9a85a6cf457c6881cc7ff8b1af15b31f70da07a4
combine_pdf (1.0.31) sha256=f0aaa687dba78d1f9b1d57622db6bdaff018480313b9dae05145ed8ca830f0bd
concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
connection_pool (2.5.5) sha256=e54ff92855753df1fd7c59fa04a398833355f27dd14c074f8c83a05f72a716ad
cookiejar (0.3.4) sha256=11b16acfc4baf7a0f463c21a6212005e04e25f5554d4d9f24d97f3492dfda0df
crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d
css_parser (1.21.1) sha256=6cfd3ffc0a97333b39d2b1b49c95397b05e0e3b684d68f77ec471ba4ec2ef7c7
csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f
cuprite (0.17) sha256=b140d5dc70d08b97ad54bcf45cd95d0bd430e291e9dffe76fff851fddd57c12b
database_cleaner (2.1.0) sha256=1dcba26e3b1576da692fc6bac10136a4744da5bcc293d248aae19640c65d89cd
database_cleaner-active_record (2.2.2) sha256=88296b9f3088c31f7c0d4fcec10f68e4b71c96698043916de59b04debec10388
database_cleaner-core (2.0.1) sha256=8646574c32162e59ed7b5258a97a208d3c44551b854e510994f24683865d846c
datafoodconsortium-connector (1.2.0) sha256=00d5f559d6e3955708dcbe8912ef0899eae03929ac4826772c82f6fea0316ee8
date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
db2fog (0.9.2)
debug (1.11.1) sha256=2e0b0ac6119f2207a6f8ac7d4a73ca8eb4e440f64da0a3136c30343146e952b6
devise (5.0.3) sha256=c4c065051cdc4ace11547b2b7f5c3c4c97d0f1269250f5fe90f614ff78f29546
devise-encryptable (0.2.0) sha256=9860caed9484030c438cfaf05831355c16c6a9612dcd1e12e84805850f7d09b6
devise-i18n (1.16.0) sha256=a33306f90f317cfe92753a000064e3ba9dec3cab2d5d01ef40d30f4b6e24e753
dfc_provider (0.0.1)
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
digest (3.2.1) sha256=ab3312b4e272d7d5dc41c564c86a25861a1f34ac5153374199a0b74861395947
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
dotenv (3.2.0) sha256=e375b83121ea7ca4ce20f214740076129ab8514cd81378161f11c03853fe619d
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
em-http-request (1.1.7) sha256=16fbc72b2a6e20c804c564ac5d12e98668c6fcef8c3b1dd2387dff505f2efdab
em-socksify (0.3.3) sha256=7d8d08a7a8acc1263173433a6b5540edd80a8a36e35a066b650c929a3a3974ed
em-synchrony (1.0.6) sha256=6e7470a684d9bbc00d61d552911b65711540bd89e95c157156f5aacdd6f306ca
email_validator (2.2.4) sha256=5ab238095bec7aef9389f230e9e0c64c5081cdf91f19d6c5cecee0a93af20604
erb (6.0.2) sha256=9fe6264d44f79422c87490a1558479bd0e7dad4dd0e317656e67ea3077b5242b
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
et-orbi (1.4.0) sha256=6c7e3c90779821f9e3b324c5e96fda9767f72995d6ae435b96678a4f3e2de8bc
eventmachine (1.2.7) sha256=994016e42aa041477ba9cff45cbe50de2047f25dd418eba003e84f0d16560972
eventmachine_httpserver (0.2.1) sha256=5db5e8a23754204d43592e5fcc2160457c57c870babe6307c4e61fc95019b809
excon (0.81.0) sha256=e7c78b2114c42e06024fb9812fc5a6b9a984ed973f585c09c67f5ae6e7b6e5a5
execjs (2.7.0) sha256=e1fae0c5c831934c47d92363b4ea66ef2951350ab91b5d8d3174342b9c2ee5fb
factory_bot (6.2.0) sha256=d181902cdda531cf6cef036001b3a700a7b5e04bac63976864530120b2ac7d13
factory_bot_rails (6.2.0) sha256=278b969666b078e76e1c972c501da9b1fac15e5b0ff328cc7ce400366164d0a1
faraday (2.14.1) sha256=a43cceedc1e39d188f4d2cdd360a8aaa6a11da0c407052e426ba8d3fb42ef61c
faraday-follow_redirects (0.4.0) sha256=d3fa1118ab1350e24035a272b4cff64948643bb7182846db89acaf87abadd5d9
faraday-net_http (3.4.2) sha256=f147758260d3526939bf57ecf911682f94926a3666502e24c69992765875906c
ferrum (0.17.1) sha256=51d591120fc593e5a13b5d9d6474389f5145bb92a91e36eab147b5d096c8cbe7
ffaker (2.25.0) sha256=e485c5adf8195aac55662875b7f515469bca46d77b60d0e7d08db6861bcbec40
ffi (1.17.3) sha256=0e9f39f7bb3934f77ad6feab49662be77e87eedcdeb2a3f5c0234c2938563d4c
flipper (1.4.1) sha256=05843240fb3093c7037549e34b046b636c765120f22e0a6268212daf44166e4d
flipper-active_record (1.4.0) sha256=a42531d81e399a2272df19fad4420ad60db2514b143a7b470431832ff50d4d8f
flipper-ui (1.4.0) sha256=1d773344249778b3f6f6874f4a0ea3e6bc997957da01e8e5ba813af666ff8fd1
fog-aws (2.0.1) sha256=e3c5fa9cbff08115f5c0bee527f7f0c171604f4980d7ffee33f863b8df5a5303
fog-core (1.45.0) sha256=e3c225fc8f5e7886e790afbc2cf7f459affb91df02d4af7b4ff28f2ee04c8408
fog-json (1.2.0) sha256=dd4f5ab362dbc72b687240bba9d2dd841d5dfe888a285797533f85c03ea548fe
fog-xml (0.1.3) sha256=5604c42649ebb0d8a31bd973aa000c2dd0127f1c1c4c174b69266a2e78e37410
foreman (0.90.0) sha256=ff675e2d47b607ac58714a6d4ac3e1ee8f06f41d8db084531c31961e2c3f117c
formatador (0.2.5) sha256=80821869ddacb79e72870ff4bb1531efacd278c04f2df26bc6b4529ee13582bd
fugit (1.12.1) sha256=5898f478ede9b415f0804e42b8f3fd53f814bd85eebffceebdbc34e1107aaf68
fuubar (2.5.1) sha256=b272a7804b282661c7fab583a3764f92543cb482c365ae39c685cd218fdd4880
geocoder (1.8.6) sha256=e0ca1554b499f466de9b003f7dff70f89a5888761c2ca68ed9f86b6e5e24e74c
globalid (1.3.0) sha256=05c639ad6eb4594522a0b07983022f04aa7254626ab69445a0e493aa3786ff11
gmaps4rails (2.1.2) sha256=09c3bf095ca8e97d1d6f3b5c9e4dd905d445b0399c5ecfd5cc53e0fafce87c2b
good_migrations (0.3.1) sha256=3f8267c00caa29689823cb8d53cbfb62cd5b0310fe6b088f5ce589243f6f797f
haml (7.2.0) sha256=87fd2b71f7feab1724337b090a7d767f5ab2d42f08c974f3ead673f18cfcd55a
haml_lint (0.72.0) sha256=23acb3f5db1602eb99bccec8009372465321702e1229017cca53272957bfc9c8
hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
hashery (2.1.2) sha256=d239cc2310401903f6b79d458c2bbef5bf74c46f3f974ae9c1061fb74a404862
hashie (5.1.0) sha256=c266471896f323c446ea8207f8ffac985d2718df0a0ba98651a3057096ca3870
highline (3.1.2) sha256=67cbd34d19f6ef11a7ee1d82ffab5d36dfd5b3be861f450fc1716c7125f4bb4a
htmlentities (4.4.2) sha256=bbafbdf69f2eca9262be4efef7e43e6a1de54c95eb600f26984f71d2fe96c5c3
http_parser.rb (0.8.1) sha256=9ae8df145b39aa5398b2f90090d651c67bd8e2ebfe4507c966579f641e11097a
i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
i18n-js (3.9.2) sha256=34c20bf3183afea475a372382ad6258be073dca5354a4bdab19b01adea3b798e
i18n-tasks (1.1.2) sha256=4dcfba49e52a623f30661cb316cb80d84fbba5cb8c6d88ef5e02545fffa3637a
image_processing (1.14.0) sha256=754cc169c9c262980889bec6bfd325ed1dafad34f85242b5a07b60af004742fb
imagen (0.2.0) sha256=369fe912078877dba92615ebfc6f35a7d833e31f24f47bdd3ad5371a4139e24b
immigrant (0.3.6) sha256=9c154dec2a2cbce62b32967a8b33e1eed58fed6432be3601f67cb88820024e96
invisible_captcha (2.3.0) sha256=309ee5a5e891ecfb732c85b12f1aa9252a648df6f2761b3b41205e824e30ff15
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
ipaddress (0.8.3) sha256=85640c4f9194c26937afc8c78e3074a8e7c97d5d1210358d1440f01034d006f5
irb (1.17.0) sha256=168c4ddb93d8a361a045c41d92b2952c7a118fa73f23fe14e55609eb7a863aae
jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1
jquery-rails (4.4.0) sha256=b1048e5bf181b94dcac82d28a4696cd79c6200167a024a07f57607ab28aa7036
jquery-ui-rails (4.2.1) sha256=5b349e7066150b16d7a784183f040c083d51af3357937b8564aa0cc8b1cd59bd
json (2.19.2) sha256=e7e1bd318b2c37c4ceee2444841c86539bc462e81f40d134cf97826cb14e83cf
json-canonicalization (1.0.0) sha256=d4848a8cca7534455c6721f2d9fc9e5e9adca49486864a898810024f67d59446
json-jwt (1.17.0) sha256=6ff99026b4c54281a9431179f76ceb81faa14772d710ef6169785199caadc4cc
json-ld (3.3.2) sha256=b9531893bf5bdc01db428e96953845a23adb1097125ce918ae0f97c4a6e1ab27
json-schema (4.1.1) sha256=89a8399745a710c2c7be3ff9564f67a0ae982c82f7590a95ab9cebe374fa2d49
json_spec (1.1.5) sha256=7a77b97a92c787e2aa3fbc4a1239afc3342c781151dc98cfb81461b3b7cad10f
jsonapi-serializer (2.2.0) sha256=f8141ac6f0c1e17e8513df68f8341afe2d7bffc285841d7090bc07f07efb0029
jwt (2.10.2) sha256=31e1ee46f7359883d5e622446969fe9c118c3da87a0b1dca765ce269c3a0c4f4
knapsack_pro (9.2.3) sha256=354b402cb08709b98f9ffa32d69e989f3bdd829a6c989649d60a9a0256421eed
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
launchy (3.0.0) sha256=4abcdab659689550ceca6ec0630cd9efd9940b51dc14cb4ebceee8f7aedc791b
letter_opener (1.10.0) sha256=2ff33f2e3b5c3c26d1959be54b395c086ca6d44826e8bf41a14ff96fdf1bdbb2
link_header (0.0.8) sha256=15c65ce43b29f739b30d05e5f25c22c23797e89cf6f905dbb595fb4c70cb55f9
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
listen (3.10.0) sha256=c6e182db62143aeccc2e1960033bebe7445309c7272061979bb098d03760c9d2
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
loofah (2.25.1) sha256=d436c73dbd0c1147b16c4a41db097942d217303e1f7728704b37e4df9f6d2e04
mail (2.9.0) sha256=6fa6673ecd71c60c2d996260f9ee3dd387d4673b8169b502134659ece6d34941
marcel (1.1.0) sha256=fdcfcfa33cc52e93c4308d40e4090a5d4ea279e160a7f6af988260fa970e0bee
matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b
method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5
mime-types (3.7.0) sha256=dcebf61c246f08e15a4de34e386ebe8233791e868564a470c3fe77c00eed5e56
mime-types-data (3.2025.0924) sha256=f276bca15e59f35767cbcf2bc10e023e9200b30bd6a572c1daf7f4cc24994728
mimemagic (0.4.3) sha256=08ac2d41e2d7cd967b00843dea8c189f91939e3f47732204944908f1ab7ecc9f
mini_magick (5.3.1) sha256=29395dfd76badcabb6403ee5aff6f681e867074f8f28ce08d78661e9e4a351c4
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289
minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d
monetize (1.13.0) sha256=0c1f8ae28aa21bbf1a6d014138d9bb0a5ce756c1a99b8b7f7d409c258b01d3b3
money (6.16.0) sha256=cf476f5f19188e247ec20f6509cff64dc35d395ee018bad799b1a9ea8bd4754a
msgpack (1.8.0) sha256=e64ce0212000d016809f5048b48eb3a65ffb169db22238fb4b72472fecb2d732
multi_json (1.19.1) sha256=7aefeff8f2c854bf739931a238e4aea64592845e0c0395c8a7d2eea7fdd631b7
multi_xml (0.6.0) sha256=d24393cf958adb226db884b976b007914a89c53ad88718e25679d7008823ad52
mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751
net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
net-imap (0.6.3) sha256=9bab75f876596d09ee7bf911a291da478e0cd6badc54dfb82874855ccc82f2ad
net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3
net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8
net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736
newrelic_rpm (9.24.0) sha256=70fb09fe684d97f161d27e430ab4baeab6c84dc73d07402130c653fe808eadb6
nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
nokogiri (1.19.2) sha256=38fdd8b59db3d5ea9e7dfb14702e882b9bf819198d5bf976f17ebce12c481756
nokogiri-html5-inference (0.3.0) sha256=04f078ad36843da11973773dbdd034672eb397373b0766ab11679e4a7b86c01e
oauth2 (1.4.11) sha256=6739fcc8872bc94f476b0cae3e8bd78a56d8364b1b79b0757794c25e152d5c10
observer (0.1.2) sha256=d8a3107131ba661138d748e7be3dbafc0d82e732fffba9fccb3d7829880950ac
omniauth (2.1.4) sha256=42a05b0496f0d22e1dd85d42aaf602f064e36bb47a6826a27ab55e5ba608763c
omniauth-rails_csrf_protection (2.0.1) sha256=c6e3204d7e3925bb537cb52d50fdfc9f05293f1a9d87c5d4ab4ca3a39ba8c32d
omniauth_openid_connect (0.8.0) sha256=1f2f3890386e2a742221cee0d2e903b78d874e6fab9ea3bfa31c1462f4793d25
openid_connect (2.3.1) sha256=5d808380cff80d78e3d3d54cfaebe2d6461d835c674faa29e2314a402c1b2182
order_management (0.0.1)
orm_adapter (0.5.0) sha256=aa5d0be5d540cbb46d3a93e88061f4ece6a25f6e97d6a47122beb84fe595e9b9
ostruct (0.6.1) sha256=09a3fb7ecc1fa4039f25418cc05ae9c82bd520472c5c6a6f515f03e4988cb817
package_json (0.2.0) sha256=92c022dc2999a9e57e835f1ec1a84c9a6c2be08e0fd68886c6f86d94101b6b4d
pagy (9.4.0) sha256=db3f2e043f684155f18f78be62a81e8d033e39b9f97b1e1a8d12ad38d7bce738
paper_trail (17.0.0) sha256=1c2842061d3874ca7015908e821e2aa14f9b982af2acb2a7974713bf79021c85
parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
paranoia (2.6.4) sha256=9d88a6589b2418eaa136ec491e5995f4f31061d7301acec14f4f0f1827b86250
parser (3.3.10.2) sha256=6f60c84aa4bdcedb6d1a2434b738fe8a8136807b6adc8f7f53b97da9bc4e9357
paypal-sdk-core (0.3.4) sha256=142cf0a5f375344acb812da47ec4906b07eb7b3a4121cb45da561bdbeff4587e
paypal-sdk-merchant (1.117.2) sha256=c78d424ab767aa7c65d023521e2d0cc80a01ef700c0e548a6b7d147d2cc40f6d
pdf-reader (2.15.1) sha256=18c6a986a84a3117fa49f4279fc2de51f5d2399b71833df5d2bccd595c7068ce
pg (1.6.3) sha256=1388d0563e13d2758c1089e35e973a3249e955c659592d10e5b77c468f628a99
pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
private_address_check (0.6.0) sha256=94ac6edefb857f7e2288695498b463aac342fb9701dad326f6555375b0c8dbef
pry (0.16.0) sha256=d76c69065698ed1f85e717bd33d7942c38a50868f6b0673c636192b3d1b6054e
psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
puffing-billy (4.0.4) sha256=87015b0c41e0722b2171a0c5aa8130fd3f58aa1c016a1dc6dc569b2028aa846f
puma (7.2.0) sha256=bf8ef4ab514a4e6d4554cb4326b2004eba5036ae05cf765cfe51aba9706a72a8
query_count (1.1.1) sha256=97204a71ae22d7bda9165ebb85f7e967ef86b3b07d5c966b42e5b22d6a41b181
raabro (1.4.0) sha256=d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
rack (2.2.23) sha256=a8fe9d7e07064770b8ec123663fded8a59ef7e2b6db5cda7173d45a5718ab69c
rack-mini-profiler (2.3.4) sha256=2c43bb1d0091c23e3efba054de5ebfb9c40a238f37f2d24dd4df7cf70d0bee3c
rack-oauth2 (2.3.0) sha256=43e02cf73f13886a0a06499603caeec58aeba6eae1fefc4977c9678b7652c632
rack-protection (3.2.0) sha256=3c74ba7fc59066453d61af9bcba5b6fe7a9b3dab6f445418d3b391d5ea8efbff
rack-proxy (0.7.7) sha256=446a4b57001022145d5c3ba73b775f66a2260eaf7420c6907483141900395c8a
rack-rewrite (1.5.1) sha256=682079619b15e4a8084377ce12887b2d14cdafff6b23120db3dca4ddc2e13456
rack-session (1.0.2) sha256=a02115e5420b4de036839b9811e3f7967d73446a554b42aa45106af335851d76
rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463
rack-timeout (0.7.0) sha256=757337e9793cca999bb73a61fe2a7d4280aa9eefbaf787ce3b98d860749c87d9
rackup (1.0.1) sha256=ba86604a28989fe1043bff20d819b360944ca08156406812dca6742b24b3c249
rails (7.1.6) sha256=9a0a335e510de3daad7542cd791af3d8ff710c644e1da17ed12e96d2f28a7470
rails-controller-testing (1.0.5) sha256=741448db59366073e86fc965ba403f881c636b79a2c39a48d0486f2607182e94
rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d
rails-erd (1.7.2) sha256=0b17d0fba25d319d8da8af7a3e5e2149d02d6187cc7351e8be43423f07c48bcd
rails-html-sanitizer (1.7.0) sha256=28b145cceaf9cc214a9874feaa183c3acba036c9592b19886e0e45efc62b1e89
rails-i18n (7.0.10) sha256=efae16e0ac28c0f42e98555c8db1327d69ab02058c8b535e0933cb106dd931ca
railties (7.1.6) sha256=2a10e97f2eaca66d11f0fef4b1f4d826e6ee28d4cf01ff16624420dd45e7de1c
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
ransack (4.1.1) sha256=01db200ede29be9f147a2c433eb0005b36df24bc7f8b624a6eeea62d50788901
rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe
rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e
rdf (3.3.1) sha256=dda6a2c95198915fc63d66ee270e35d4a76d431720747a2cf97ecd92062fa150
rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
redis (5.4.1) sha256=b5e675b57ad22b15c9bcc765d5ac26f60b675408af916d31527af9bd5a81faae
redis-client (0.26.4) sha256=3ad70beff5da2653e02dfeae996e7d8d7147a558da12b16b2282ad345e4c7120
regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
request_store (1.7.0) sha256=e1b75d5346a315f452242a68c937ef8e48b215b9453a77a6c0acdca2934c88cb
responders (3.2.0) sha256=89c2d6ac0ae16f6458a11524cae4a8efdceba1a3baea164d28ee9046bd3df55a
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
roadie (5.2.1) sha256=e4a4f61ce792bd91b228b6844b4bad6b160cdc1b8df86c81a8b983082a5001d6
roadie-rails (3.4.0) sha256=f7b02bd3b74051eaa51ebb636049c4c9fc54cf2a68234eafc5a5fb78ad1f9aa9
rodf (1.2.0) sha256=0c10b48c57033473f7bfca8d825e04296b13642cfaacca470121bbd28d1e2991
roo (2.10.1) sha256=cbb43bc955f9c110e74b721c835fb9bd3515b63af88ec709ac87fbf30f8be70e
rspec (3.13.0) sha256=d490914ac1d5a5a64a0e1400c1d54ddd2a501324d703b8cfe83f458337bab993
rspec-core (3.13.5) sha256=ab3f682897c6131c67f9a17cfee5022a597f283aebe654d329a565f9937a4fa3
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
rspec-mocks (3.13.5) sha256=e4338a6f285ada9fe56f5893f5457783af8194f5d08884d17a87321d5195ea81
rspec-rails (7.1.1) sha256=e15dccabed211e2fd92f21330c819adcbeb1591c1d66c580d8f2d8288557e331
rspec-retry (0.6.2) sha256=6101ba23a38809811ae3484acde4ab481c54d846ac66d5037ccb40131a60d858
rspec-sql (0.0.3) sha256=132a5e1ca722b3b0d5f7ad5ae6e644fdb4a1e0a4cf7ce020a5fd89ddc37766b4
rspec-support (3.13.6) sha256=2e8de3702427eab064c9352fe74488cc12a1bfae887ad8b91cba480ec9f8afb2
rswag (2.17.0) sha256=aba267bd205f0f4f8db53277182897f125890338731971641add050f7911414c
rswag-api (2.17.0) sha256=728b336b65168ab8ab6024b0e5d267b485c22ccdeb9dfbfb6ec3bac423545a13
rswag-specs (2.17.0) sha256=a3b2bdf6df89f8741fe4a4ee47ceb1e77dc13e1c96bbe07352117d6e61afa9e3
rswag-ui (2.17.0) sha256=5f707b9b5e8171ddf9f519f6e401e79e419bd1d07387508603e76124f2443212
rubocop (1.84.2) sha256=5692cea54168f3dc8cb79a6fe95c5424b7ea893c707ad7a4307b0585e88dbf5f
rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd
rubocop-capybara (2.22.1) sha256=ced88caef23efea53f46e098ff352f8fc1068c649606ca75cb74650970f51c0c
rubocop-factory_bot (2.28.0) sha256=4b17fc02124444173317e131759d195b0d762844a71a29fe8139c1105d92f0cb
rubocop-rails (2.34.3) sha256=10d37989024865ecda8199f311f3faca990143fbac967de943f88aca11eb9ad2
rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2
rubocop-rspec_rails (2.32.0) sha256=4a0d641c72f6ebb957534f539d9d0a62c47abd8ce0d0aeee1ef4701e892a9100
ruby-graphviz (1.2.5) sha256=1c2bb44e3f6da9e2b829f5e7fd8d75a521485fb6b4d1fc66ff0f93f906121504
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
ruby-rc4 (0.1.5) sha256=00cc40a39d20b53f5459e7ea006a92cf584e9bc275e2a6f7aa1515510e896c03
ruby-vips (2.2.5) sha256=f3c547a172c36ba26b8614c809f5823bc6199623ec6204ec7c3bce29037f7758
rubyzip (2.4.1) sha256=8577c88edc1fde8935eb91064c5cb1aef9ad5494b940cf19c775ee833e075615
rufus-scheduler (3.9.2) sha256=55fa9e4db0ff69d7f38c804f17baba0c9bce5cba39984ae3c5cf6c039d1323b9
rugged (1.9.0) sha256=7faaa912c5888d6e348d20fa31209b6409f1574346b1b80e309dbc7e8d63efac
sanitize (7.0.0) sha256=269d1b9d7326e69307723af5643ec032ff86ad616e72a3b36d301ac75a273984
sd_notify (0.1.1) sha256=cbc7ac6caa7cedd26b30a72b5eeb6f36050dc0752df263452ea24fb5a4ad3131
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
select2-rails (3.4.9)
semantic_range (3.1.0) sha256=9fc01ee40f2e5f81042e95d421837f688177dd603753b5eab41ff9bba50a34a1
shakapacker (8.4.0) sha256=df234ed4a9173407adc288df0d7f156c485ada9214b23d8f0e2c734f11c1aca8
shoulda-matchers (7.0.1) sha256=b4bfd8744c10e0a36c8ac1a687f921ee7e25ed529e50488d61b79a8688749c77
sidekiq (7.3.10) sha256=781eb4f65ef36042534ad73d72f211283afb7fee82eec786ada4ed1972ef8e3c
sidekiq-scheduler (6.0.1) sha256=e1fc04e851a3e2d9896bc6ab00f03d790fef8923bb56915e5b141804bca34062
simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
spreadsheet_architect (5.1.0) sha256=5000ae41a0a40270b9334da02cd9f5a6ca6aec1e867348eaa2263913860d9159
spring (4.4.2) sha256=22f61bacd8dc8595cedcdc738de46d7fc18be4d7a770986760344c924f485ce7
spring-commands-rspec (1.0.4) sha256=6202e54fa4767452e3641461a83347645af478bf45dddcca9737b43af0dd1a2c
spring-commands-rubocop (0.4.0) sha256=3e677a2c8a27ae8a986f04bfb69e66d5d55b017541e8be93bf0dc48a7f5690c1
spring-watcher-listen (2.1.0) sha256=e958bcd956d1026eb95d16b2ef0e241d1ca35a754628bd45040e3d9954a61949
sprockets (3.7.5) sha256=72c20f256548f8a37fe7db41d96be86c3262fddaf4ebe9d69ec8317394fed383
sprockets-rails (3.5.2) sha256=a9e88e6ce9f8c912d349aa5401509165ec42326baf9e942a85de4b76dbc4119e
state_machines (0.100.4) sha256=dd4e555ebb061569dd8fe18a5f39b51d10be6306b89a0c315a9697671d752565
state_machines-activemodel (0.31.0) sha256=82465856736fa6e3ddd76b8dba9e17c82d0823027ec1fbc18432f47817be4500
state_machines-activerecord (0.31.0) sha256=c8cc334ba4ea202a2feaaa42aaa43d360e5e111c1aff1e2fa156fe232ca31181
stimulus_reflex (3.5.5) sha256=cf08eddccc75c4b8f61446da73972b8bd9304f173f4881f500202d5c3f70e3c9
stimulus_reflex_testing (0.3.1)
stringex (2.8.6) sha256=c7b382d2b2a47a1e1646f256df201c48d487d6296fbb289d76802f67f5e929c4
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
stripe (15.5.0) sha256=e61cac450194df863697039559e314bad41c1b0e10c627039c28019349a570d0
swd (2.0.3) sha256=4cdbe2a4246c19f093fce22e967ec3ebdd4657d37673672e621bf0c7eb770655
sysexits (1.2.0) sha256=598241c4ae57baa403c125182dfdcc0d1ac4c0fb606dd47fbed57e4aaf795662
taler (0.3.0) sha256=905da226ad420eda7ebe0e57b91086efac51a0f11205aaab671168534e02a559
temple (0.10.4) sha256=b7a1e94b6f09038ab0b6e4fe0126996055da2c38bec53a8a336f075748fff72c
terminal-table (4.0.0) sha256=f504793203f8251b2ea7c7068333053f0beeea26093ec9962e62ea79f94301d2
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
thread-local (1.1.0) sha256=2fb568d31f0ef278063f45a3923c5ed62ee5c33da8134103bc7d2ecdb87bd2e7
tilt (2.7.0) sha256=0d5b9ba69f6a36490c64b0eee9f6e9aad517e20dcc848800a06eb116f08c6ab3
timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
ttfunk (1.7.0) sha256=2370ba484b1891c70bdcafd3448cfd82a32dd794802d81d720a64c15d3ef2a96
turbo-rails (2.0.23) sha256=ee0d90733aafff056cf51ff11e803d65e43cae258cc55f6492020ec1f9f9315f
turbo_power (0.7.0) sha256=ad95d147e0fa761d0023ad9ca00528c7b7ddf6bba8ca2e23755d5b21b290d967
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
undercover (0.8.4) sha256=99a6df93a6bb2c8d06e701df5fdb3be9411f50eae3f9f9e1d67672176258d09c
unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
uniform_notifier (1.18.0) sha256=4787785556f66f6418486da0f1d78b3239aaff98e2e7938fb05e2062b0ffce9d
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
valid_email2 (7.0.15) sha256=f3fe1c4b87b9a405bb2e8de060b736cce168e476e4c72725c5d05e11321ff4a1
validate_url (1.0.15) sha256=72fe164c0713d63a9970bd6700bea948babbfbdcec392f2342b6704042f57451
validates_lengths_from_database (0.8.0) sha256=a4cfbbd49673da331c4f098ade90893dde531cae7ca37da41b8f79e15bc0538b
vcr (6.4.0) sha256=077ac92cc16efc5904eb90492a18153b5e6ca5398046d8a249a7c96a9ea24ae6
view_component (4.5.0) sha256=0d951360d830752da4d1daa5f6cb643f17c30f295fb779fd4de90a051537d9c1
view_component_reflex (3.1.14.pre9) sha256=d1f9e61fdeb2f949fe292aba263b4c05a81e531b0ce2cd58297ad55353bbbaa7
virtual_assembly-semantizer (1.1.1) sha256=a8ce46cd525ce7875a07da7f513c7045cb9c2ee6c32980f7245ffd0d718ba94f
warden (1.2.9) sha256=46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0
web (0.0.1)
web-console (4.2.1) sha256=e7bcf37a10ea2b4ec4281649d1cee461b32232d0a447e82c786e6841fd22fe20
webfinger (2.1.3) sha256=567a52bde77fb38ca6b67e55db755f988766ec4651c1d24916a65aa70540695c
webmock (3.26.2) sha256=774556f2ea6371846cca68c01769b2eac0d134492d21f6d0ab5dd643965a4c90
webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131
websocket-driver (0.8.0) sha256=ed0dba4b943c22f17f9a734817e808bc84cdce6a7e22045f5315aa57676d4962
websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241
wicked_pdf (2.8.1)
wkhtmltopdf-binary (0.12.6.10) sha256=863060a4559253d20f9a73c76106bd972a443aec9fdd5013b45e1d1a9a44f352
xml-simple (1.1.8) sha256=3b53afc963c0b952892dcfc0d30de24bda2cef3c861b37e8c286c6c5e0ab97ba
xpath (3.2.0) sha256=6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e
zeitwerk (2.7.5) sha256=d8da92128c09ea6ec62c949011b00ed4a20242b255293dd66bf41545398f73dd
RUBY VERSION
ruby 3.4.8p72

View File

@@ -19,7 +19,9 @@
background-color: white;
box-shadow: $box-shadow;
border-radius: 3px;
width: max-content;
min-width: 80px;
max-width: 110px;
display: none;
z-index: 100;
@@ -27,6 +29,15 @@
display: block;
}
// Fade out so user can see which option was selected
&.selected {
transition:
opacity 0.2s linear,
visibility 0.2s linear;
opacity: 0;
visibility: hidden;
}
& > a {
display: block;
padding: 5px 10px;

View File

@@ -6,6 +6,9 @@ export default class extends Controller {
connect() {
super.connect();
window.addEventListener("click", this.#hideIfClickedOutside);
// Close menu when making a selection
this.contentTarget.addEventListener("click", this.#selected);
}
disconnect() {
@@ -13,17 +16,22 @@ export default class extends Controller {
}
toggle() {
this.contentTarget.classList.toggle("show");
this.#toggleShow();
}
#selected = () => {
this.contentTarget.classList.add("selected");
};
#hideIfClickedOutside = (event) => {
if (this.element.contains(event.target)) {
return;
}
this.#hide();
this.#toggleShow(false);
};
#hide() {
this.contentTarget.classList.remove("show");
#toggleShow(force = undefined) {
this.contentTarget.classList.toggle("show", force);
this.contentTarget.classList.remove("selected");
}
}

View File

@@ -0,0 +1,56 @@
# frozen_string_literal: true
module Admin
class AjaxSearchController < Spree::Admin::BaseController
def producers
query = OpenFoodNetwork::Permissions.new(spree_current_user)
.managed_product_enterprises.is_primary_producer.by_name
render json: build_search_response(query)
end
def categories
query = Spree::Taxon.all
render json: build_search_response(query)
end
def tax_categories
query = Spree::TaxCategory.all
render json: build_search_response(query)
end
private
def build_search_response(query)
page = (params[:page] || 1).to_i
per_page = 30
filtered_query = apply_search_filter(query)
total_count = filtered_query.size
items = paginated_items(filtered_query, page, per_page)
results = format_results(items)
{ results: results, pagination: { more: (page * per_page) < total_count } }
end
def apply_search_filter(query)
search_term = params[:q]
return query if search_term.blank?
escaped_search_term = ActiveRecord::Base.sanitize_sql_like(search_term)
pattern = "%#{escaped_search_term}%"
query.where('name ILIKE ?', pattern)
end
def paginated_items(query, page, per_page)
query.order(:name).offset((page - 1) * per_page).limit(per_page).pluck(:name, :id)
end
def format_results(items)
items.map { |label, value| { value:, label: } }
end
end
end

View File

@@ -107,7 +107,7 @@ module Spree
def cancel
@order = Spree::Order.find_by!(number: params[:id])
authorize! :cancel, @order
authorize! :cancel, @order, session[:access_token]
if Orders::CustomerCancellationService.new(@order).call
flash[:success] = I18n.t(:orders_your_order_has_been_cancelled)

View File

@@ -52,5 +52,15 @@ module Admin
@allowed_source_producers ||= OpenFoodNetwork::Permissions.new(spree_current_user)
.enterprises_granting_linked_variants
end
# Query only name of the model to avoid loading the whole record
def selected_option(id, model)
return [] unless id
name = model.where(id: id).pick(:name)
return [] unless name
[[name, id]]
end
end
end

15
app/jobs/rake_job.rb Normal file
View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
require "rake"
# Executes a rake task
class RakeJob < ApplicationJob
def perform(task_string)
Rails.application.load_tasks if Rake::Task.tasks.empty?
Rake.application.invoke_task(task_string)
ensure
name, _args = Rake.application.parse_task_string(task_string)
Rake::Task[name].reenable
end
end

View File

@@ -113,7 +113,11 @@ module Spree
item.order.changes_allowed?
end
can [:cancel, :bulk_cancel], Spree::Order do |order|
can :cancel, Spree::Order do |order, token|
order.user == user || (order.token && token == order.token)
end
can :bulk_cancel, Spree::Order do |order|
order.user == user
end
@@ -225,9 +229,17 @@ module Spree
OpenFoodNetwork::Permissions.new(user).
enterprises_granting_linked_variants.include? variant.supplier
end
can [
:admin,
:index,
:bulk_update,
:destroy,
:destroy_variant,
:clone,
:create_linked_variant
], :products_v3
can [:admin, :index, :bulk_update, :destroy, :destroy_variant, :clone,
:create_linked_variant], :products_v3
can [:admin, :producers, :categories, :tax_categories], :ajax_search
can [:create], Spree::Variant
can [:admin, :index, :read, :edit,

View File

@@ -9,14 +9,22 @@
- if producer_options.many?
.producers
= label_tag :producer_id, t('.producers.label')
= select_tag :producer_id, options_for_select(producer_options, producer_id),
include_blank: t('.all_producers'), class: "fullwidth",
data: { "controller": "tom-select", 'tom-select-placeholder-value': t('.search_for_producers')}
= render(SearchableDropdownComponent.new(name: :producer_id,
aria_label: t('.producers.label'),
options: selected_option(producer_id, Enterprise),
selected_option: producer_id,
remote_url: admin_ajax_search_producers_url,
include_blank: t('.all_producers'),
placeholder_value: t('.search_for_producers')))
.categories
= label_tag :category_id, t('.categories.label')
= select_tag :category_id, options_for_select(category_options, category_id),
include_blank: t('.all_categories'), class: "fullwidth",
data: { "controller": "tom-select", 'tom-select-placeholder-value': t('.search_for_categories')}
= render(SearchableDropdownComponent.new(name: :category_id,
aria_label: t('.categories.label'),
options: selected_option(category_id, Spree::Taxon),
selected_option: category_id,
remote_url: admin_ajax_search_categories_url,
include_blank: t('.all_categories'),
placeholder_value: t('.search_for_categories')))
-if variant_tag_enabled?(spree_current_user)
.tags
= label_tag :tags_name_in, t('.tags.label')

View File

@@ -4,7 +4,7 @@
%td.col-image
-# empty
- variant.source_variants.each do |source_variant|
= content_tag(:span, "🔗", title: t('admin.products_page.variant_row.sourced_from', source_name: source_variant.display_name, source_id: source_variant.id, hub_name: variant.hub&.name))
= content_tag(:span, "🔗", title: t('admin.products_page.variant_row.sourced_from', source_name: source_variant.full_name, source_id: source_variant.id, hub_name: variant.hub&.name))
%td.col-name.field.naked_inputs
= f.hidden_field :id
= f.text_field :display_name, 'aria-label': t('admin.products_page.columns.name'), placeholder: variant.product.name
@@ -59,27 +59,28 @@
= render(SearchableDropdownComponent.new(form: f,
name: :supplier_id,
aria_label: t('.producer_field_name'),
options: producer_options,
options: variant.supplier_id ? [[variant.supplier.name, variant.supplier_id]] : [],
selected_option: variant.supplier_id,
include_blank: t('admin.products_v3.filters.select_producer'),
remote_url: admin_ajax_search_producers_url,
placeholder_value: t('admin.products_v3.filters.select_producer')))
= error_message_on variant, :supplier
%td.col-category.field.naked_inputs
= render(SearchableDropdownComponent.new(form: f,
name: :primary_taxon_id,
options: category_options,
options: variant.primary_taxon_id ? [[variant.primary_taxon.name, variant.primary_taxon_id]] : [],
selected_option: variant.primary_taxon_id,
aria_label: t('.category_field_name'),
include_blank: t('admin.products_v3.filters.select_category'),
remote_url: admin_ajax_search_categories_url,
placeholder_value: t('admin.products_v3.filters.select_category')))
= error_message_on variant, :primary_taxon
%td.col-tax_category.field.naked_inputs
= render(SearchableDropdownComponent.new(form: f,
name: :tax_category_id,
options: tax_category_options,
options: variant.tax_category_id ? [[variant.tax_category.name, variant.tax_category_id]] : [],
selected_option: variant.tax_category_id,
include_blank: t('.none_tax_category'),
aria_label: t('.tax_category_field_name'),
include_blank: t('.none_tax_category'),
remote_url: admin_ajax_search_tax_categories_url,
placeholder_value: t('.search_for_tax_categories')))
= error_message_on variant, :tax_category
- if variant_tag_enabled?(spree_current_user)
@@ -92,7 +93,7 @@
- if variant.persisted?
= link_to t('admin.products_page.actions.edit'), edit_admin_product_variant_path(variant.product, variant)
- if allowed_source_producers.include?(variant.supplier)
- if variant.source_variants.empty? && allowed_source_producers.include?(variant.supplier)
= link_to t('admin.products_page.actions.create_linked_variant'), admin_create_linked_variant_path(variant_id: variant.id, product_index:), 'data-turbo-method': :post
- if variant.product.variants.size > 1

View File

@@ -83,6 +83,9 @@ export default class extends Controller {
}
#addRemoteOptions(options) {
// by default, for dropdown_input plugin, it's true. Otherwise for multi-select it's false
// it should always be true so to invoke the onDropdownOpen to fetch options
options.shouldOpen = true;
this.openedByClick = false;
options.firstUrl = (query) => {
@@ -91,12 +94,9 @@ export default class extends Controller {
options.load = this.#fetchOptions.bind(this);
options.onFocus = function () {
this.control.load("", () => {});
}.bind(this);
options.onDropdownOpen = function () {
this.openedByClick = true;
this.control.load("", () => {});
}.bind(this);
options.onType = function () {

View File

@@ -375,6 +375,6 @@
.slide-in {
transform-origin: top;
animation: slideInTop 0.5s forwards;
animation: slideInTop 0.5s;
}
}

View File

@@ -7,6 +7,15 @@ redis_connection_settings = {
Sidekiq.configure_server do |config|
config.redis = redis_connection_settings
config.on(:startup) do
# Load schedule file similar to sidekiq/cli.rb loading the main config.
path = File.expand_path("../sidekiq_scheduler.yml", __dir__)
erb = ERB.new(File.read(path), trim_mode: "-")
Sidekiq.schedule =
YAML.safe_load(erb.result, permitted_classes: [Symbol], aliases: true)
SidekiqScheduler::Scheduler.instance.reload_schedule!
end
end
Sidekiq.configure_client do |config|

View File

@@ -193,6 +193,7 @@ de_DE:
not_available_to_shop: "ist nicht verfügbar für %{shop}"
card_details: "Kreditkartendaten"
card_type: "Kreditkartentyp"
use_new_cc: "Eine neue Kreditkarte verwenden"
cardholder_name: "Kreditkarteninhaber"
community_forum_url: "URL des Community-Forums"
customer_instructions: "Informationen für Kunden"
@@ -665,6 +666,7 @@ de_DE:
bill_address: "Rechnungsadresse"
ship_address: "Lieferadresse"
balance: "Saldo"
credit: "Verfügbares Guthaben"
update_address_success: "Adresse wurde erfolgreich aktualisiert."
update_address_error: "Bitte füllen Sie alle erforderlichen Felder aus."
edit_bill_address: "Rechnungsadresse bearbeiten"
@@ -683,6 +685,7 @@ de_DE:
has_associated_subscriptions: "Löschen fehlgeschlagen: Dieser Kunde hat aktive Abonnements. Bitte kündigen Sie diese zuerst."
customer_account_transaction:
index:
available_credit: "Verfügbares Guthaben: %{available_credit}"
description: Beschreibung
amount: Summe
running_balance: Fortlaufender Saldo
@@ -2203,6 +2206,7 @@ de_DE:
order_total: 'Gesamtsumme:'
order_payment: "Zahlungsart:"
no_payment_required: "Keine Zahlung erforderlich."
credit_used: "Verwendetes Guthaben: %{amount}"
customer_credit: Guthaben
order_billing_address: Rechnungsadresse
order_delivery_on: Lieferung am
@@ -4363,7 +4367,7 @@ de_DE:
date_picker:
flatpickr_date_format: "d.m.Y"
flatpickr_datetime_format: "d.m.Y H:i"
today: "heute"
today: "Heute"
now: "Jetzt"
close: "Schließen"
orders:

View File

@@ -208,6 +208,10 @@ hu:
no_default_card: "^Ennél az ügyfélnél nem áll rendelkezésre alapértelmezett kártya"
shipping_method:
not_available_to_shop: "nem érhető el a %{shop} számára"
user_invitation:
attributes:
email:
is_already_manager: már menedzser
card_details: "A kártya adatai"
card_type: "Kártyatípus"
card_type_is: "A kártya típusa: "
@@ -513,7 +517,7 @@ hu:
create: "Létrehozás"
cancel: "Mégsem"
cancel_order: "Törlés"
resume: "Összefoglaló"
resume: "Visszaállítás"
save: "Mentés"
edit: "Szerkesztés"
update: "Frissítés"
@@ -570,8 +574,11 @@ hu:
delete: Törlés
remove: Eltávolítás
preview: Előnézet
create_linked_variant: Kapcsolt termékváltozat létrehozása
image:
edit: Szerkesztés
variant_row:
sourced_from: "Forrás: %{source_name} ( %{source_id} ); Elosztó: %{hub_name}"
product_preview:
product_preview: Termék előnézet
shop_tab: Átvételi pont
@@ -1051,7 +1058,7 @@ hu:
back_to_my_inventory: Vissza a leltárhoz
orders:
edit:
order_sure_want_to: Biztosan %{event} akarod ezt a megrendelést?
order_sure_want_to: 'Biztosan végrehajtod a rendelésen ezt a műveletet? %{event} '
voucher_tax_included_in_price: "%{label}(a kupon tartalmazza az adót)"
tax_on_fees: "Díjak adója"
invoice_email_sent: 'Számla e-mail elküldve'
@@ -1561,7 +1568,7 @@ hu:
coordinator: Koordinátor
orders_close: A rendelések lezárulnak
row:
suppliers: beszállítók
suppliers: beszállító
distributors: elosztó
variants: változat
simple_form:

View File

@@ -83,6 +83,13 @@ Openfoodnetwork::Application.routes.draw do
delete 'products_v3/destroy_variant/:id', to: 'products_v3#destroy_variant', as: 'destroy_variant'
post 'clone/:id', to: 'products_v3#clone', as: 'clone_product'
post 'products/create_linked_variant', to: 'products_v3#create_linked_variant', as: 'create_linked_variant'
scope :ajax_search, as: :ajax_search, controller: :ajax_search do
get :producers
get :categories
get :tax_categories
end
resources :product_preview, only: [:show]
resources :variant_overrides do

View File

@@ -1,24 +0,0 @@
# Force manual loading of rails application to get all env variables from dotenv-rails when running whenever cmd
require File.expand_path('../environment', __FILE__)
require 'whenever'
require 'yaml'
# Learn more: http://github.com/javan/whenever
env "MAILTO", ENV["SCHEDULE_NOTIFICATIONS"] if ENV["SCHEDULE_NOTIFICATIONS"]
# If we use -e with a file containing specs, rspec interprets it and filters out our examples
job_type :run_file, "cd :path; :environment_variable=:environment bundle exec script/rails runner :task :output"
every 1.month, at: '4:30am' do
rake 'ofn:data:remove_transient_data'
end
every 1.day, at: '2:45am' do
rake 'db2fog:clean' if ENV['S3_BACKUPS_BUCKET']
end
every 4.hours do
rake 'db2fog:backup' if ENV['S3_BACKUPS_BUCKET']
end

View File

@@ -7,15 +7,8 @@
- default
- mailers
:scheduler:
:schedule:
HeartbeatJob:
every: ["5m", first_in: "0s"]
SubscriptionPlacementJob:
every: "5m"
SubscriptionConfirmJob:
every: "5m"
TriggerOrderCyclesToOpenJob:
every: "5m"
OrderCycleClosingJob:
every: "5m"
# This config is loaded by sidekiq before dotenv is loading our server config.
# Therefore we load the schedule later. See:
#
# - config/initializers/sidekiq.rb
# - config/sidekiq_scheduler.yml

View File

@@ -0,0 +1,36 @@
# Configure sidekiq-scheduler to run jobs.
#
# - https://github.com/sidekiq-scheduler/sidekiq-scheduler
#
# > Note that every and interval count from when the Sidekiq process (re)starts.
# > So every: '48h' will never run if the Sidekiq process is restarted daily,
# > for example. You can do every: ['48h', first_in: '0s'] to make the job run
# > immediately after a restart, and then have the worker check when it was
# > last run.
#
# Therefore, we use `cron` for jobs that should run at certain times like backups.
HeartbeatJob:
every: ["5m", first_in: "0s"]
SubscriptionPlacementJob:
every: "5m"
SubscriptionConfirmJob:
every: "5m"
TriggerOrderCyclesToOpenJob:
every: "5m"
OrderCycleClosingJob:
every: "5m"
backup:
class: "RakeJob"
args: ["db2fog:backup"]
cron: "0 */4 * * *" # every 4 hours
enabled: <%= ENV.fetch("S3_BACKUPS_BUCKET", false) && true %>
backup_clean:
class: "RakeJob"
args: ["db2fog:clean"]
cron: "45 2 * * *" # every day at 2:45am
enabled: <%= ENV.fetch("S3_BACKUPS_BUCKET", false) && true %>
ofn_clean:
class: "RakeJob"
args: ["ofn:data:remove_transient_data"]
cron: "30 4 1 * *" # every month on the first at 4:30am

View File

@@ -6,7 +6,7 @@
set -o pipefail
echo "::group:: Running prettier with reviewdog 🐶 ..."
echo -e "\nRunning prettier with reviewdog 🐶 ..."
"$(npm root)/.bin/prettier" --check . 2>&1 | sed --regexp-extended 's/(\[warn\].*)$/\1 File is not properly formatted./' \
| reviewdog \
@@ -25,7 +25,7 @@ echo "::group:: Running prettier with reviewdog 🐶 ..."
prettier=$?
echo "::group:: Running rubocop with reviewdog 🐶 ..."
echo -e "\nRunning rubocop with reviewdog 🐶 ..."
bundle exec rubocop \
--fail-level info \
@@ -39,7 +39,7 @@ bundle exec rubocop \
rubocop=$?
echo "::group:: Running haml-lint with reviewdog 🐶 ..."
echo -e "\nRunning haml-lint with reviewdog 🐶 ..."
bundle exec haml-lint \
--fail-level warning \

View File

@@ -461,14 +461,34 @@ RSpec.describe Spree::OrdersController do
end
end
context "when a guest user has the order token in session" do
let(:order) {
create(:completed_order_with_totals, user: nil, email: "guest@example.com",
distributor: create(:distributor_enterprise))
}
before do
allow(controller).to receive(:spree_current_user) { nil }
session[:access_token] = order.token
end
it "cancels the order and redirects to the order page" do
request.env['HTTP_REFERER'] = order_path(order)
spree_put :cancel, params
expect(response.body).to match(order_path(order)).and match("redirect")
expect(flash[:success]).to eq 'Your order has been cancelled'
end
end
context "when the user has permission to cancel the order" do
before { allow(controller).to receive(:spree_current_user) { user } }
context "when the order is not yet complete" do
it "responds with forbidden" do
request.env['HTTP_REFERER'] = order_path(order)
spree_put :cancel, params
expect(response).to have_http_status(:found)
expect(response.body).to match(order_path(order)).and match("redirect")
expect(flash[:error]).to eq 'Sorry, the order could not be cancelled'
end
@@ -481,9 +501,9 @@ RSpec.describe Spree::OrdersController do
}
it "responds with success" do
request.env['HTTP_REFERER'] = order_path(order)
spree_put :cancel, params
expect(response).to have_http_status(:found)
expect(response.body).to match(order_path(order)).and match("redirect")
expect(flash[:success]).to eq 'Your order has been cancelled'
end

View File

@@ -166,7 +166,6 @@ describe("TomSelectController", () => {
expect(settings.searchField).toBe("label");
expect(settings.load).toEqual(expect.any(Function));
expect(settings.firstUrl).toEqual(expect.any(Function));
expect(settings.onFocus).toEqual(expect.any(Function));
});
it("fetches page 1 on focus", async () => {

View File

@@ -16,12 +16,13 @@ describe("VerticalEllipsisMenuController test", () => {
<div data-controller="vertical-ellipsis-menu" id="root">
<div data-action="click->vertical-ellipsis-menu#toggle" id="button">...</div>
<div data-vertical-ellipsis-menu-target="content" id="content">
<a href="#" id="item"></a>
</div>
</div>
`;
const button = document.getElementById("button");
const content = document.getElementById("content");
const item = document.getElementById("item");
});
it("add show class to content when toggle is called", () => {
@@ -43,4 +44,15 @@ describe("VerticalEllipsisMenuController test", () => {
document.body.click();
expect(content.classList.contains("show")).toBe(false);
});
it("adds selected class to content when clicking a menu item", () => {
button.click();
expect(content.classList.contains("selected")).toBe(false);
item.click();
expect(content.classList.contains("selected")).toBe(true);
// and removes it again when clicking button again
button.click();
expect(content.classList.contains("selected")).toBe(false);
});
});

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
require "tasks/data/remove_transient_data"
RSpec.describe RakeJob do
let(:task_string) { "ofn:data:remove_transient_data" }
it "calls the removal service" do
expect(RemoveTransientData).to receive(:new).and_call_original
RakeJob.perform_now(task_string)
end
it "can be called several times" do
expect(RemoveTransientData).to receive(:new).twice.and_call_original
RakeJob.perform_now(task_string)
RakeJob.perform_now(task_string)
end
end

View File

@@ -0,0 +1,285 @@
# frozen_string_literal: true
RSpec.describe "/admin/ajax_search" do
include AuthenticationHelper
let(:admin_user) { create(:admin_user) }
let(:regular_user) { create(:user) }
describe "GET /admin/ajax_search/producers" do
context "when user is not logged in" do
it "redirects to login" do
get admin_ajax_search_producers_path
expect(response).to redirect_to %r|#/login$|
end
end
context "when user is logged in without permissions" do
before { login_as regular_user }
it "redirects to unauthorized" do
get admin_ajax_search_producers_path
expect(response).to redirect_to('/unauthorized')
end
end
context "when user is an admin" do
before { login_as admin_user }
let!(:producer1) { create(:supplier_enterprise, name: "Apple Farm") }
let!(:producer2) { create(:supplier_enterprise, name: "Berry Farm") }
let!(:producer3) { create(:supplier_enterprise, name: "Cherry Orchard") }
let!(:distributor) { create(:distributor_enterprise, name: "Distributor") }
it "returns producers sorted alphabetically by name" do
get admin_ajax_search_producers_path
expect(response).to have_http_status(:ok)
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Apple Farm', 'Berry Farm',
'Cherry Orchard'])
expect(json_response["pagination"]["more"]).to be false
end
it "filters producers by search query" do
get admin_ajax_search_producers_path, params: { q: "berry" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Berry Farm'])
expect(json_response["results"].pluck("value")).to eq([producer2.id])
end
it "filters are case insensitive" do
get admin_ajax_search_producers_path, params: { q: "BERRY" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Berry Farm'])
end
it "filters with partial matches" do
get admin_ajax_search_producers_path, params: { q: "Farm" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Apple Farm', 'Berry Farm'])
end
it "excludes non-producer enterprises" do
get admin_ajax_search_producers_path
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).not_to include('Distributor')
end
context "with more than 30 producers" do
before do
create_list(:supplier_enterprise, 35) do |enterprise, i|
enterprise.update!(name: "Producer #{(i + 1).to_s.rjust(2, '0')}")
end
end
it "returns first page with 30 results and more flag as true" do
get admin_ajax_search_producers_path, params: { page: 1 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(30)
expect(json_response["pagination"]["more"]).to be true
end
it "returns remaining results on second page with more flag as false" do
get admin_ajax_search_producers_path, params: { page: 2 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(8)
expect(json_response["pagination"]["more"]).to be false
end
end
end
context "when user has enterprise permissions" do
let!(:my_producer) { create(:supplier_enterprise, name: "My Producer") }
let!(:other_producer) { create(:supplier_enterprise, name: "Other Producer") }
let(:user_with_producer) { create(:user, enterprises: [my_producer]) }
before { login_as user_with_producer }
it "returns only managed producers" do
get admin_ajax_search_producers_path
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['My Producer'])
expect(json_response["results"].pluck("label")).not_to include('Other Producer')
end
end
end
describe "GET /admin/ajax_search/categories" do
context "when user is not logged in" do
it "redirects to login" do
get admin_ajax_search_categories_path
expect(response).to redirect_to %r|#/login$|
end
end
context "when user is logged in without permissions" do
before { login_as regular_user }
it "redirects to unauthorized" do
get admin_ajax_search_categories_path
expect(response).to redirect_to('/unauthorized')
end
end
context "when user is an admin" do
before { login_as admin_user }
let!(:category1) { create(:taxon, name: "Vegetables") }
let!(:category2) { create(:taxon, name: "Fruits") }
let!(:category3) { create(:taxon, name: "Dairy") }
it "returns categories sorted alphabetically by name" do
get admin_ajax_search_categories_path
expect(response).to have_http_status(:ok)
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Dairy', 'Fruits', 'Vegetables'])
expect(json_response["pagination"]["more"]).to be false
end
it "filters categories by search query" do
get admin_ajax_search_categories_path, params: { q: "fruit" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Fruits'])
expect(json_response["results"].pluck("value")).to eq([category2.id])
end
it "filters are case insensitive" do
get admin_ajax_search_categories_path, params: { q: "VEGETABLES" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Vegetables'])
end
it "filters with partial matches" do
get admin_ajax_search_categories_path, params: { q: "ege" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Vegetables'])
end
context "with more than 30 categories" do
before do
create_list(:taxon, 35) do |taxon, i|
taxon.update!(name: "Category #{(i + 1).to_s.rjust(2, '0')}")
end
end
it "returns first page with 30 results and more flag as true" do
get admin_ajax_search_categories_path, params: { page: 1 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(30)
expect(json_response["pagination"]["more"]).to be true
end
it "returns remaining results on second page with more flag as false" do
get admin_ajax_search_categories_path, params: { page: 2 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(8)
expect(json_response["pagination"]["more"]).to be false
end
end
end
end
describe "GET /admin/ajax_search/tax_categories" do
context "when user is not logged in" do
it "redirects to login" do
get admin_ajax_search_tax_categories_path
expect(response).to redirect_to %r|#/login$|
end
end
context "when user is logged in without permissions" do
before { login_as regular_user }
it "redirects to unauthorized" do
get admin_ajax_search_tax_categories_path
expect(response).to redirect_to('/unauthorized')
end
end
context "when user is an admin" do
before { login_as admin_user }
let!(:tax_cat1) { create(:tax_category, name: "GST") }
let!(:tax_cat2) { create(:tax_category, name: "VAT") }
let!(:tax_cat3) { create(:tax_category, name: "No Tax") }
it "returns tax categories sorted alphabetically by name" do
get admin_ajax_search_tax_categories_path
expect(response).to have_http_status(:ok)
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['GST', 'No Tax', 'VAT'])
expect(json_response["pagination"]["more"]).to be false
end
it "filters tax categories by search query" do
get admin_ajax_search_tax_categories_path, params: { q: "vat" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['VAT'])
expect(json_response["results"].pluck("value")).to eq([tax_cat2.id])
end
it "filters are case insensitive" do
get admin_ajax_search_tax_categories_path, params: { q: "GST" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['GST'])
end
it "filters with partial matches" do
get admin_ajax_search_tax_categories_path, params: { q: "tax" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['No Tax'])
end
context "with more than 30 tax categories" do
before do
create_list(:tax_category, 35) do |tax_cat, i|
tax_cat.update!(name: "Tax Category #{(i + 1).to_s.rjust(2, '0')}")
end
end
it "returns first page with 30 results and more flag as true" do
get admin_ajax_search_tax_categories_path, params: { page: 1 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(30)
expect(json_response["pagination"]["more"]).to be true
end
it "returns remaining results on second page with more flag as false" do
get admin_ajax_search_tax_categories_path, params: { page: 2 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(8)
expect(json_response["pagination"]["more"]).to be false
end
end
end
end
end

View File

@@ -19,12 +19,25 @@ module TomSelectHelper
tomselect_wrapper.find(:css, '.ts-dropdown div.create').click
end
# Searches for and selects an option in a TomSelect dropdown with search functionality.
# @param value [String] The text to search for and select from the dropdown
# @param options [Hash] Configuration options
# @option options [String] :from The name/id of the select field
# @option options [Boolean] :remote_search If true, waits for search loading after interactions
#
# @example
# tomselect_search_and_select("Apple", from: "fruit_selector")
# tomselect_search_and_select("California", from: "state", remote_search: true)
def tomselect_search_and_select(value, options)
tomselect_wrapper = page.find_field(options[:from]).sibling(".ts-wrapper")
tomselect_wrapper.find(".ts-control").click
expect_tomselect_loading_completion(tomselect_wrapper, options)
# Use send_keys as setting the value directly doesn't trigger the search
tomselect_wrapper.find(".ts-dropdown input.dropdown-input").send_keys(value)
tomselect_wrapper.find(".ts-dropdown .ts-dropdown-content .option.active", text: value).click
expect_tomselect_loading_completion(tomselect_wrapper, options)
tomselect_wrapper.find(".ts-dropdown .ts-dropdown-content .option", text: value).click
end
def tomselect_select(value, options)
@@ -64,4 +77,52 @@ module TomSelectHelper
end
end
end
# Validates both available options and selected options in a TomSelect dropdown.
# @param from [String] The name/id of the select field
# @param existing_options [Array<String>] List of options that should be available in the dropdown
# @param selected_options [Array<String>] List of options that should currently be selected
#
# @example
# expect_tomselect_existing_with_selected_options(
# from: "category_selector",
# existing_options: ["Fruit", "Vegetables", "Dairy"],
# selected_options: ["Fruit"]
# )
def expect_tomselect_existing_with_selected_options(from:, existing_options:, selected_options:)
tomselect_wrapper = page.find_field(from).sibling(".ts-wrapper")
tomselect_control = tomselect_wrapper.find('.ts-control')
tomselect_control.click # open the dropdown (would work for remote vs non-remote dropdowns)
# validate existing options are present in the dropdown
within(tomselect_wrapper) do
existing_options.each do |option|
expect(page).to have_css(
".ts-dropdown .ts-dropdown-content .option",
text: option
)
end
end
# validate selected options are selected in the dropdown
within(tomselect_wrapper) do
selected_options.each do |option|
expect(page).to have_css(
"div[data-ts-item]",
text: option
)
end
end
# close the dropdown by clicking on the already selected option
tomselect_wrapper.find(".ts-dropdown .ts-dropdown-content .option.active").click
end
def expect_tomselect_loading_completion(tomselect_wrapper, options)
return unless options[:remote_search]
expect(tomselect_wrapper).to have_css(".spinner")
expect(tomselect_wrapper).not_to have_css(".spinner")
end
end

View File

@@ -15,10 +15,6 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
login_as user
end
let(:producer_search_selector) { 'input[placeholder="Select producer"]' }
let(:categories_search_selector) { 'input[placeholder="Select category"]' }
let(:tax_categories_search_selector) { 'input[placeholder="Search for tax categories"]' }
describe "column selector" do
let!(:product) { create(:simple_product) }
@@ -102,54 +98,7 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
}
let!(:product_a) { create(:simple_product, name: "Apples", sku: "APL-00") }
context "when they are under 11" do
before do
create_list(:supplier_enterprise, 9, users: [user])
create_list(:tax_category, 9)
create_list(:taxon, 2)
visit admin_products_url
end
it "should not display search input, change the producers, category and tax category" do
producer_to_select = random_producer(variant_a1)
category_to_select = random_category(variant_a1)
tax_category_to_select = random_tax_category
within row_containing_name(variant_a1.display_name) do
validate_tomselect_without_search!(
page, "Producer",
producer_search_selector
)
tomselect_select(producer_to_select, from: "Producer")
end
within row_containing_name(variant_a1.display_name) do
validate_tomselect_without_search!(
page, "Category",
categories_search_selector
)
tomselect_select(category_to_select, from: "Category")
validate_tomselect_without_search!(
page, "Tax Category",
tax_categories_search_selector
)
tomselect_select(tax_category_to_select, from: "Tax Category")
end
click_button "Save changes"
expect(page).to have_content "Changes saved"
variant_a1.reload
expect(variant_a1.supplier.name).to eq(producer_to_select)
expect(variant_a1.primary_taxon.name).to eq(category_to_select)
expect(variant_a1.tax_category.name).to eq(tax_category_to_select)
end
end
context "when they are over 11" do
context "when there are products" do
before do
create_list(:supplier_enterprise, 11, users: [user])
create_list(:tax_category, 11)
@@ -167,9 +116,13 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
tax_category_to_select = random_tax_category
within row_containing_name(variant_a1.display_name) do
tomselect_search_and_select(producer_to_select, from: "Producer")
tomselect_search_and_select(category_to_select, from: "Category")
tomselect_search_and_select(tax_category_to_select, from: "Tax Category")
tomselect_search_and_select(producer_to_select, from: "Producer", remote_search: true)
tomselect_search_and_select(category_to_select, from: "Category", remote_search: true)
tomselect_search_and_select(
tax_category_to_select,
from: "Tax Category",
remote_search: true
)
end
click_button "Save changes"
@@ -285,8 +238,9 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
end
describe "Create linked variant" do
let!(:variant) {
create(:variant, display_name: "My box", supplier: producer)
let!(:variant) { create(:variant, display_name: "My box", supplier: producer) }
let!(:linked_variant) {
variant.create_linked_variant(user).tap{ |v| v.update! display_name: "My linked variant" }
}
let!(:other_producer) { create(:supplier_enterprise) }
let!(:other_variant) {
@@ -312,6 +266,15 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
expect(page).to have_link "Create linked variant"
end
close_action_menu
# Check my own linked variant
within row_containing_name("My linked variant") do
page.find(".vertical-ellipsis-menu").click
expect(page).not_to have_link "Create linked variant"
end
close_action_menu
# Create linked variant sourced from my friend
within row_containing_name("My friends box") do
@@ -332,12 +295,11 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
# Close action menu (shouldn't need this, it should close itself)
last_box.click
# And I can perform actions on the new product
# And I can perform actions on the new variant
within last_box do
page.find(".vertical-ellipsis-menu").click
expect(page).to have_link "Edit"
# expect(page).to have_link "Clone" # tofix: menu is partially obscured
# expect(page).to have_link "Delete" # it's not a proper link
expect(page).to have_selector "a", text: "Delete" # it's not a proper link
fill_in "Name", with: "My copy of Apples"
end
@@ -376,12 +338,10 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
}
describe "Actions columns (delete)" do
before do
visit admin_products_url
end
it "shows an actions menu with a delete link when clicking on icon for product. " \
"doesn't show delete link for the single variant" do
visit admin_products_url
within product_selector do
page.find(".vertical-ellipsis-menu").click
expect(page).to have_css(delete_option_selector)

View File

@@ -86,14 +86,14 @@ RSpec.describe 'As an enterprise user, I can manage my products' do
find('button[aria-label="On Hand"]').click
find('input[id$="_price"]').fill_in with: "11.1"
select supplier.name, from: 'Producer'
select taxon.name, from: 'Category'
if stock == "on_hand"
find('input[id$="_on_hand_desired"]').fill_in with: "66"
elsif stock == "on_demand"
find('input[id$="_on_demand_desired"]').check
end
tomselect_select supplier.name, from: 'Producer'
tomselect_select taxon.name, from: 'Category'
end
expect(page).to have_content "1 product modified."

View File

@@ -106,13 +106,19 @@ RSpec.describe 'As an enterprise user, I can browse my products' do
visit spree.admin_products_path
within row_containing_name "Variant1" do
expect(page).to have_select "Producer", with_options: ["Producer A", "Producer B"],
selected: "Producer A"
expect_tomselect_existing_with_selected_options(
from: 'Producer',
existing_options: ["Producer A", "Producer B"],
selected_options: ["Producer A"]
)
end
within row_containing_name "Variant2a" do
expect(page).to have_select "Producer", with_options: ["Producer A", "Producer B"],
selected: "Producer B"
expect_tomselect_existing_with_selected_options(
from: 'Producer',
existing_options: ["Producer A", "Producer B"],
selected_options: ["Producer B"]
)
end
end
end
@@ -543,24 +549,21 @@ RSpec.describe 'As an enterprise user, I can browse my products' do
it "shows only suppliers that I manage or have permission to" do
visit spree.admin_products_path
existing_options = [supplier_managed1.name, supplier_managed2.name, supplier_permitted.name]
within row_containing_placeholder(product_supplied.name) do
expect(page).to have_select(
'_products_0_variants_attributes_0_supplier_id',
options: [
'Select producer',
supplier_managed1.name, supplier_managed2.name, supplier_permitted.name
], selected: supplier_managed1.name
expect_tomselect_existing_with_selected_options(
existing_options:,
from: '_products_0_variants_attributes_0_supplier_id',
selected_options: [supplier_managed1.name]
)
end
within row_containing_placeholder(product_supplied_permitted.name) do
expect(page).to have_select(
'_products_1_variants_attributes_0_supplier_id',
options: [
'Select producer',
supplier_managed1.name, supplier_managed2.name, supplier_permitted.name
], selected: supplier_permitted.name
expect_tomselect_existing_with_selected_options(
existing_options:,
from: '_products_1_variants_attributes_0_supplier_id',
selected_options: [supplier_permitted.name]
)
end
end

View File

@@ -350,8 +350,8 @@ RSpec.describe 'As an enterprise user, I can update my products' do
click_on "On Hand" # activate popout
fill_in "On Hand", with: "3"
select producer.name, from: 'Producer'
select taxon.name, from: 'Category'
tomselect_select producer.name, from: 'Producer'
tomselect_select taxon.name, from: 'Category'
end
expect {
@@ -586,8 +586,8 @@ RSpec.describe 'As an enterprise user, I can update my products' do
fill_in "Name", with: "Nice box"
fill_in "SKU", with: "APL-02"
select producer.name, from: 'Producer'
select taxon.name, from: 'Category'
tomselect_select producer.name, from: 'Producer'
tomselect_select taxon.name, from: 'Category'
end
expect {

View File

@@ -17,7 +17,7 @@ RSpec.describe "admin/products_v3/_filters.html.haml" do
end
let(:spree_current_user) { build(:enterprise_user) }
it "shows the producer filter when there are options" do
it "shows the producer filter with the default option initially" do
allow(view).to receive_messages locals.merge(
producer_options: [
["Ada's Apples", 1],
@@ -27,9 +27,7 @@ RSpec.describe "admin/products_v3/_filters.html.haml" do
is_expected.to have_content "Producers"
is_expected.to have_select "producer_id", options: [
"All producers",
"Ada's Apples",
"Ben's Bananas",
"All producers"
], selected: nil
end

View File

@@ -5015,9 +5015,9 @@ lodash.uniq@^4.5.0:
integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
lodash@^4.17.21:
version "4.17.23"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a"
integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==
version "4.18.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c"
integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==
log4js@^6.4.1:
version "6.9.1"
@@ -5151,9 +5151,9 @@ mimic-fn@^2.1.0:
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
mini-css-extract-plugin@^2.9.4:
version "2.10.1"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.1.tgz#a7f0bb890f4e1ce6dfc124bd1e6d6fcd3b359844"
integrity sha512-k7G3Y5QOegl380tXmZ68foBRRjE9Ljavx835ObdvmZjQ639izvZD8CS7BkWw1qKPPzHsGL/JDhl0uyU1zc2rJw==
version "2.10.2"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz#5c85ec9450c05d26e32531b465a15a08c3a57253"
integrity sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==
dependencies:
schema-utils "^4.0.0"
tapable "^2.2.1"
@@ -5270,9 +5270,9 @@ node-addon-api@^7.0.0:
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
node-forge@^1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.3.tgz#0ad80f6333b3a0045e827ac20b7f735f93716751"
integrity sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==
version "1.4.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.4.0.tgz#1c7b7d8bdc2d078739f58287d589d903a11b2fc2"
integrity sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==
node-int64@^0.4.0:
version "0.4.0"
@@ -7111,9 +7111,9 @@ tr46@^5.1.0:
punycode "^2.3.1"
trix@*:
version "2.1.17"
resolved "https://registry.yarnpkg.com/trix/-/trix-2.1.17.tgz#a47c4ee1925a7abb26aee5c094ec7ef9fe49575a"
integrity sha512-nkHg7VgIItGVx1CFA645dDlAoCgah+9gv80Yc+97aS8jkZmO5K4MxkSqmU9t/C8upqZB8uhfJs90epoDfYz/6Q==
version "2.1.18"
resolved "https://registry.yarnpkg.com/trix/-/trix-2.1.18.tgz#d87a5be64c10ecdab64f5af47f941b5318c08225"
integrity sha512-DWOdTsz3n9PO3YBc1R6pGh9MG1cXys/2+rouc/qsISncjc2MBew2UOW8nXh3NjUOjobKsXCIPR6LB02abg2EYg==
dependencies:
dompurify "^3.2.5"