Compare commits

..

1 Commits

Author SHA1 Message Date
Jean-Baptiste Bellet
a8f88ba687 Workaround: bad interaction btw Turbo and stripe iframe
Found this workaround here: https://github.com/hotwired/turbo/issues/270#issuecomment-1068130327

This js hack persists the original iframe object downloaded from stripe in the new body sent by Turbo (instead of using the new one, in the new body)
2023-01-09 10:35:52 +01:00
1431 changed files with 28363 additions and 55090 deletions

View File

@@ -2,4 +2,3 @@
.gitignore
log/*
tmp/*
node_modules/

6
.env
View File

@@ -61,9 +61,3 @@ SMTP_PASSWORD="f00d"
# STRIPE_INSTANCE_PUBLISHABLE_KEY="pk_test_xxxx" # This can be a test key or a live key
# STRIPE_CLIENT_ID="ca_xxxx" # This can be a development ID or a production ID
# STRIPE_ENDPOINT_SECRET="whsec_xxxx"
# New relic settings
# see: https://one.eu.newrelic.com/admin-portal/, Administration > API keys to get the license key
# NEW_RELIC_AGENT_ENABLED=true
# NEW_RELIC_APP_NAME="Open Food Network"
# NEW_RELIC_LICENSE_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@@ -5,17 +5,9 @@
#
# cp .env.development .env.local
VERBOSE_QUERY_LOGS=true
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
OFN_REDIS_URL="redis://localhost:6379/1"
OFN_REDIS_JOBS_URL="redis://localhost:6379/2"
SITE_URL="0.0.0.0:3000"
# Deactivate rack-timeout in development.
# https://github.com/zombocom/rack-timeout#configuring
RACK_TIMEOUT_SERVICE_TIMEOUT="0"
RACK_TIMEOUT_WAIT_TIMEOUT="0"
RACK_TIMEOUT_WAIT_OVERTIME="0"

11
.gitattributes vendored
View File

@@ -1,11 +0,0 @@
# Set default behavior to automatically normalize line endings.
* text=auto
# Set line endings to LF, even on Windows. Otherwise, execution within Docker fails.
# See https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings#per-repository-settings
*.sh text eol=lf
# Same thing for following files, but they don't have an sh extension
pre-commit eol=lf
webpack-dev-server eol=lf
install-bundler eol=lf

View File

@@ -7,23 +7,24 @@ assignees: ''
---
## 1. Preparation on Thursday
## Preparation on Thursday
- [ ] Merge pull requests in the [Ready To Go] column
- [ ] Include translations: `tx pull --force`
- [ ] [Draft new release]. Look at previous [releases] for inspiration.
- [ ] Notify [#instance-managers] of user-facing changes.
## 2. Testing
## Testing
- [ ] [Find build] of the release commit and copy it below.
- [ ] Move this issue to Test Ready.
- [ ] Notify `@testers` in [#testing].
- [ ] Test build: <!-- paste build link here, e.g. https://semaphore...builds/1234 -->
## 3. Finish on Tuesday
## Finish on Tuesday
- [ ] Publish and notify [#global-community] (this is automatically posted with a plugin)
- [ ] Publish and notify [#global-community]:
> The next release is ready: https://github.com/openfoodfoundation/openfoodnetwork/releases/latest
- [ ] Deploy the new release to all managed instances.
<details><summary>Command line instructions</summary>
<pre>
@@ -40,7 +41,7 @@ The full process is described at https://github.com/openfoodfoundation/openfoodn
[Ready To Go]: #zenhub
[Transifex pull request]: https://github.com/openfoodfoundation/openfoodnetwork/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+head%3Atransifex
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A%23%23+User+facing+changes+:eyes:%0A%0A%0A%23%23%23+Experimental+features+for+testing+:sunglasses:%0A%0A%0A%23%23+Technical+changes+:wrench:%0A%0A
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A%23%23+User+facing+changes+:eyes:%0A%0A%0A%0A%23%23+Technical+changes+:wrench:%0A%0A
[releases]: https://github.com/openfoodfoundation/openfoodnetwork/releases
[#instance-managers]: https://app.slack.com/client/T02G54U79/CG7NJ966B
[#testing]: https://openfoodnetwork.slack.com/app_redirect?channel=C02TZ6X00

View File

@@ -6,12 +6,10 @@ updates:
schedule:
interval: "daily"
open-pull-requests-limit: 10
# Only specific requirements are specified in Gemfile, so don't touch it.
versioning-strategy: lockfile-only
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
# All versions are specified in package.json, so please update them.
versioning-strategy: increase
versioning-strategy: lockfile-only

View File

@@ -33,7 +33,7 @@ jobs:
- name: Setup Brakeman
env:
BRAKEMAN_VERSION: '5.4.0'
BRAKEMAN_VERSION: '4.10' # SARIF support is provided in Brakeman version 4.10+
run: |
gem install brakeman --version $BRAKEMAN_VERSION
@@ -41,7 +41,6 @@ jobs:
- name: Scan
continue-on-error: true
run: |
git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
brakeman -f sarif -o output.sarif.json .
# Upload the SARIF file generated in the previous step

View File

@@ -18,7 +18,7 @@ permissions:
jobs:
knapsack_rspec_controllers:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -57,7 +57,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
@@ -68,6 +68,7 @@ jobs:
bundle exec rake db:schema:load
- name: Run tests
env:
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: 864ef557d85ea8e603e086c0387d5154
KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
@@ -81,12 +82,12 @@ jobs:
# https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it
#KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES: true
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/controllers/**/*_spec.rb}"
run: |
git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
bundle exec rake knapsack_pro:rspec
knapsack_rspec_models:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -125,7 +126,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
@@ -155,7 +156,7 @@ jobs:
bundle exec rake knapsack_pro:rspec
knapsack_rspec_system_admin:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -194,7 +195,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
@@ -233,7 +234,7 @@ jobs:
if-no-files-found: ignore
knapsack_rspec_system_consumer:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -272,7 +273,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
@@ -311,7 +312,7 @@ jobs:
if-no-files-found: ignore
knapsack_rspec_engines:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -350,7 +351,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
@@ -389,7 +390,7 @@ jobs:
if-no-files-found: ignore
knapsack_rspec_test_the_rest:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -428,7 +429,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
@@ -459,7 +460,7 @@ jobs:
bundle exec rake knapsack_pro:rspec
non_knapsack_jest_karma:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -488,7 +489,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile

View File

@@ -9,11 +9,7 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v1
- uses: ruby/setup-ruby@v1
- run: git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
- name: rubocop
uses: reviewdog/action-rubocop@v2
with:
@@ -28,16 +24,6 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- run: git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
- name: prettier
uses: EPMatt/reviewdog-action-prettier@v1
with:

View File

@@ -1,62 +0,0 @@
name: "Deploy to Staging"
on:
pull_request_target:
types: [labeled]
workflow_dispatch:
inputs:
server:
description: "Staging Server"
type: choice
required: true
options:
- staging.openfoodnetwork.org.uk
- staging.openfoodnetwork.org.au
- staging.coopcircuits.fr
jobs:
deploy_pr:
if: contains(fromJSON('["pr-staged-uk", "pr-staged-au", "pr-staged-fr"]'), github.event.label.name)
runs-on: ubuntu-latest
steps:
- name: "Check user has write access"
uses: "lannonbr/repo-permission-check-action@2.0.2"
with:
permission: "write"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Configure deployment key
if: success()
run: |
install -m 600 -D /dev/null ~/.ssh/id_rsa
echo "${{ secrets.DEPLOYMENT_KEY }}" > ~/.ssh/id_rsa
echo "${{ secrets.DEPLOYMENT_HOSTS }}" > ~/.ssh/known_hosts
- name: Deploy to Staging
if: success()
run: |
ssh ofn-deploy@${{ github.event.label.description }} -o LogLevel=ERROR "pull-request-${{ github.event.pull_request.number }} ."
deploy_branch:
if: ${{ inputs.server }}
runs-on: ubuntu-latest
steps:
- name: "Check user has write access"
uses: "lannonbr/repo-permission-check-action@2.0.2"
with:
permission: "write"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Configure deployment key
if: success()
run: |
install -m 600 -D /dev/null ~/.ssh/id_rsa
echo "${{ secrets.DEPLOYMENT_KEY }}" > ~/.ssh/id_rsa
echo "${{ secrets.DEPLOYMENT_HOSTS }}" > ~/.ssh/known_hosts
- name: Deploy to Staging
if: success()
run: |
ssh ofn-deploy@${{ inputs.server }} -o LogLevel=ERROR "$GITHUB_REF_NAME $GITHUB_SHA"

View File

@@ -1 +1 @@
17.9.1
14.16.1

View File

@@ -1,27 +1,17 @@
# Ignore a lot of things, but we should enable where it can be helpful.
# Basically, ignore everythings expect app/webpacker/controllers/*.js and app/webpacker/packs/*.js
*.css
*.scss
# Except v2
!/app/webpacker/css/admin/v2/**/*.scss
*.md
*.yml
*.yaml
*.json
*.html
# JS
# Enabled: app/webpacker/controllers/*.js and app/webpacker/packs/*.js
babel.config.js
postcss.config.js
# SCSS
# Enabled: most of admin
/app/webpacker/css/admin/globals/mixins.scss
/app/webpacker/css/admin/globals/variables.scss
/app/webpacker/css/admin/shared/
/app/webpacker/css/admin_v3/globals/variables.scss
/app/webpacker/css/darkswarm/
/app/webpacker/css/mail/
/app/webpacker/css/shared/
# More
/app/assets/
/config/
/coverage/

View File

@@ -1,3 +1 @@
{
"printWidth": 100
}
{}

View File

@@ -2,8 +2,9 @@
#
# These are the rules we agreed upon and we work towards.
AllCops:
NewCops: enable
NewCops: disable
SuggestExtensions: false
TargetRailsVersion: 5.0
Exclude:
- 'bin/**/*'
- 'db/**/*'
@@ -25,7 +26,6 @@ Metrics/BlockLength:
AllowedMethods: [
"class_eval",
"collection",
"configure",
"context",
"delete",
"describe",
@@ -41,13 +41,9 @@ Metrics/BlockLength:
"resources",
"scenario",
"shared_examples",
"shared_examples_for",
"xdescribe",
]
Metrics/ParameterLists:
CountKeywordArgs: false
Rails/ApplicationRecord:
Exclude:
# Migrations should not contain application code:
@@ -93,8 +89,6 @@ Naming/VariableNumber:
AllowedIdentifiers:
- street_address_1
- street_address_2
AllowedPatterns:
- _v[\d]+
Bundler/DuplicatedGem:
Enabled: false

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
3.1.4
3.0.3

View File

@@ -57,7 +57,6 @@ TL;DR:
* Maintain a clean commit history
* Use a style consistent with the rest of the codebase
* Before submitting, [rebase your work][rebase] on the current master branch
* After submitting, be sure to check the [CI test results](ci). Click on a ❌ result to view the logged results and investigate.
From here, your pull request will progress through the [Review, Test, Merge & Deploy process][process].
@@ -71,5 +70,4 @@ From here, your pull request will progress through the [Review, Test, Merge & De
[slack-dev]: https://openfoodnetwork.slack.com/messages/C2GQ45KNU
[ofn-transifex]: https://www.transifex.com/open-food-foundation/open-food-network/
[i18n]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Internationalisation-%28i18n%29
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/5
[ci]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Continuous-Integration
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/2

View File

@@ -28,18 +28,14 @@ RUN apt-get update && apt-get install -y \
gnupg
# Setup ENV variables
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:/usr/local/src/nodenv/shims:/usr/local/src/nodenv/bin:$PATH
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:$PATH
ENV RBENV_ROOT /usr/local/src/rbenv
ENV NODENV_ROOT /usr/local/src/nodenv
ENV CONFIGURE_OPTS --disable-install-doc
ENV BUNDLE_PATH /bundles
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so
WORKDIR /usr/src/app
# trim spaces and line return from .ruby-version file
COPY .ruby-version .ruby-version.raw
RUN cat .ruby-version.raw | tr -d '\r\t ' > .ruby-version
COPY .ruby-version .
# Install Rbenv & Ruby
RUN git clone --depth 1 https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
@@ -54,19 +50,10 @@ RUN sh -c "echo 'deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-
apt-get update && \
apt-get install -yqq --no-install-recommends postgresql-client-10 libpq-dev
# trim spaces and line return from .node-version file
COPY .node-version .node-version.raw
RUN cat .node-version.raw | tr -d '\r\t ' > .node-version
# Install Node and Yarn with Nodenv
RUN git clone --depth 1 https://github.com/nodenv/nodenv.git ${NODENV_ROOT} && \
git clone --depth 1 https://github.com/nodenv/node-build.git ${NODENV_ROOT}/plugins/node-build && \
git clone --depth 1 https://github.com/pine/nodenv-yarn-install.git ${NODENV_ROOT}/plugins/nodenv-yarn-install && \
git clone --depth 1 https://github.com/nodenv/nodenv-package-rehash.git ${NODENV_ROOT}/plugins/nodenv-package-rehash && \
echo 'eval "$(nodenv init -)"' >> /etc/profile.d/nodenv.sh && \
nodenv install $(cat .node-version) && \
nodenv global $(cat .node-version)
# Install NodeJs and yarn
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - \
&& apt-get install --no-install-recommends -y nodejs \
&& npm install -g yarn
# Install Chrome
RUN wget --quiet -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \

View File

@@ -11,9 +11,6 @@ Head to our wiki on [Learning Rails](https://github.com/openfoodfoundation/openf
The fastest way to make it work locally is to use Docker, you only need to setup git, see the [Docker setup guide](docker/README.md).
Otherwise, for a local setup you will need:
* Ruby and bundler (check current Ruby version in [.ruby-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.ruby-version) file)
- To manage versions, it's recommended to use [rbenv](https://github.com/rbenv/rbenv) or [RVM](https://rvm.io/)
* Node and yarn (check current Node version in [.node-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.node-version) file)
- [nodevn](https://github.com/nodenv/nodenv) is recommended.
* PostgreSQL database
* Redis (for background jobs)
* Chrome (for testing)
@@ -23,13 +20,15 @@ The following guides will provide OS-specific step-by-step instructions to get t
- [Debian Setup Guide][debian]
- [OSX Setup Guide][osx]
If you are likely to need to manage multiple version of ruby on your local machine, we recommend version managers such as [rbenv](https://github.com/rbenv/rbenv) or [RVM](https://rvm.io/).
For those new to Rails, the following tutorial will help get you up to speed with configuring a [Rails environment](http://guides.rubyonrails.org/getting_started.html).
### Get it
So you have set up your local environment according to the requirements listed above. If you're planning on contributing code to the project (which we [LOVE](CONTRIBUTING.md)), it is a good idea to begin by forking this repo using the `Fork` button in the top-right corner of this screen. You should then be able to use `git clone` to copy your fork onto your local machine:
git clone git@github.com:YOUR_GITHUB_USERNAME_HERE/openfoodnetwork.git
git clone https://github.com/YOUR_GITHUB_USERNAME_HERE/openfoodnetwork
Jump into your new local copy of the Open Food Network:
@@ -37,7 +36,7 @@ Jump into your new local copy of the Open Food Network:
And then add an `upstream` remote that points to the main repo:
git remote add upstream git@github.com:openfoodfoundation/openfoodnetwork.git
git remote add upstream https://github.com/openfoodfoundation/openfoodnetwork
Fetch the latest version of `master` from `upstream` (ie. the main repo):
@@ -48,14 +47,14 @@ Fetch the latest version of `master` from `upstream` (ie. the main repo):
First, you need to create the database user the app will use by manually typing the following in your terminal:
```sh
sudo --login --user=postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"
$ sudo -u postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"
```
This will create the "ofn" user as superuser and allowing it to create databases. If this command fails, check the [troubleshooting section](#creating-the-database) for an alternative.
Next, it is _strongly recommended_ to run the setup script.
```sh
./script/setup
$ script/setup
```
If the script succeeds you're ready to start developing. If not, take a look at the output as it should be informative enough to help you troubleshoot.
@@ -114,13 +113,13 @@ Below are fixes to potential issues that can happen during the installation proc
#### Creating the database
If the `sudo -u postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"` command doesn't work, you can run the following commands instead:
If the ```$ sudo -u postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"``` command doesn't work, you can run the following commands instead:
```
createuser --superuser --pwprompt ofn
# Enter password for new role: f00d
# Enter it again: f00d
createdb open_food_network_dev --owner=ofn
createdb open_food_network_test --owner=ofn
$ createuser --superuser --pwprompt ofn
Enter password for new role: f00d
Enter it again: f00d
$ createdb open_food_network_dev --owner=ofn
$ createdb open_food_network_test --owner=ofn
```
If these commands succeed, you should be able to [continue the setup process](#get-it-running).

49
Gemfile
View File

@@ -1,13 +1,12 @@
# frozen_string_literal: true
source 'https://rubygems.org'
ruby "3.0.3"
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
ruby File.read('.ruby-version').chomp
gem 'dotenv-rails', require: 'dotenv/rails-now' # Load ENV vars before other gems
gem 'rails'
gem 'rails', '>= 6.1.4'
# Active Storage
gem "active_storage_validations"
@@ -15,11 +14,11 @@ gem "aws-sdk-s3", require: false
gem "image_processing"
gem 'activemerchant', '>= 1.78.0'
gem 'rexml'
gem 'angular-rails-templates', '>= 0.3.0'
gem 'awesome_nested_set'
gem 'ransack', '~> 2.6.0'
gem 'ransack', '2.4.2'
gem 'responders'
gem 'rexml'
gem 'webpacker', '~> 5'
gem 'i18n'
@@ -28,7 +27,7 @@ gem 'rails-i18n'
gem 'rails_safe_tasks', '~> 1.0'
gem "activerecord-import"
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-7"
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-6"
gem "fog-aws", "~> 2.0" # db2fog does not support v3
gem "mime-types" # required by fog
@@ -64,16 +63,15 @@ gem 'devise-token_authenticatable'
gem 'jwt', '~> 2.3'
gem 'oauth2', '~> 1.4.7' # Used for Stripe Connect
gem 'datafoodconsortium-connector'
gem 'jsonapi-serializer'
gem 'pagy', '~> 5.1'
gem 'rswag-api'
gem 'rswag-ui'
gem 'omniauth_openid_connect'
gem 'omniauth-rails_csrf_protection'
gem 'gitlab-omniauth-openid-connect', require: 'omniauth_openid_connect'
gem 'openid_connect', '~> 1.3'
gem 'omniauth-rails_csrf_protection'
gem 'angularjs-rails', '1.8.0'
gem 'bugsnag'
@@ -85,16 +83,16 @@ gem 'actionpack-action_caching'
# AMS is deprecated, we will introduce an alternative at some point
gem "active_model_serializers", "0.8.4"
gem 'activerecord-session_store'
gem 'acts-as-taggable-on'
gem 'acts-as-taggable-on', '~> 8.1'
gem 'angularjs-file-upload-rails', '~> 2.4.1'
gem 'bigdecimal', '3.0.2'
gem 'bootsnap', require: false
gem 'geocoder'
gem 'gmaps4rails'
gem 'mimemagic', '> 0.3.5'
gem 'paper_trail', '~> 12.1'
gem 'paper_trail', '~> 12.1.0'
gem 'rack-rewrite'
gem 'rack-timeout'
gem 'rack-ssl', require: 'rack/ssl'
gem 'roadie-rails'
gem 'hiredis'
@@ -103,27 +101,33 @@ gem 'redis', '>= 4.0', require: ['redis', 'redis/connection/hiredis']
gem 'sidekiq'
gem 'sidekiq-scheduler'
gem "cable_ready", "5.0.1"
gem "stimulus_reflex", "3.5.0.rc3"
gem "cable_ready", "5.0.0.pre9"
gem "stimulus_reflex", "3.5.0.pre9"
gem 'combine_pdf'
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
gem 'immigrant'
gem 'roo' # read spreadsheets
gem 'spreadsheet_architect' # write spreadsheets
gem 'roo'
gem 'spreadsheet_architect'
gem 'whenever', require: false
gem 'test-unit', '~> 3.5'
gem 'coffee-rails', '~> 5.0.0'
gem 'mini_racer', '0.4.0'
gem 'angular_rails_csrf'
gem 'jquery-rails', '4.4.0'
gem 'jquery-ui-rails', '~> 4.2'
gem "select2-rails", github: "openfoodfoundation/select2-rails", branch: "v349_with_thor_v1"
gem 'ofn-qz', github: 'openfoodfoundation/ofn-qz', branch: 'ofn-rails-4'
gem 'good_migrations'
gem 'flipper'
@@ -135,12 +139,9 @@ gem 'view_component_reflex', '3.1.14.pre9'
gem 'mini_portile2', '~> 2.8'
gem "faraday"
gem "private_address_check"
gem 'newrelic_rpm'
group :production, :staging do
gem 'ddtrace'
gem 'rack-timeout'
gem 'sd_notify' # For better Systemd process management. Used by Puma.
end
@@ -149,7 +150,6 @@ group :test, :development do
gem 'capybara'
gem 'cuprite'
gem 'database_cleaner', require: false
gem 'debug', '>= 1.0.0'
gem "factory_bot_rails", '6.2.0', require: false
gem 'fuubar', '~> 2.5.1'
gem 'json_spec', '~> 1.1.4'
@@ -160,6 +160,7 @@ group :test, :development do
gem 'rswag-specs'
gem 'shoulda-matchers'
gem 'timecop'
gem 'debug', '>= 1.0.0'
end
group :test do
@@ -174,16 +175,16 @@ end
group :development do
gem 'debugger-linecache'
gem 'rails-erd'
gem 'foreman'
gem 'listen'
gem 'pry', '~> 0.13.0'
gem 'query_count'
gem 'rails-erd'
gem 'rubocop'
gem 'rubocop-rails'
gem 'spring'
gem 'spring-commands-rspec'
gem 'web-console'
gem 'rack-mini-profiler', '< 3.0.0'
end

View File

@@ -1,12 +1,19 @@
GIT
remote: https://github.com/openfoodfoundation/db2fog.git
revision: 6e88c0ab9eeb23d7ad9964b27becb1c04a83141f
branch: rails-7
revision: 5b63343847452f52aa42f7fc169d6ab3af57cda3
branch: rails-6
specs:
db2fog (0.9.2)
activerecord (>= 3.2.0)
activerecord (>= 3.2.0, < 7.0)
fog-core (~> 1.0)
rails (>= 3.2.0)
rails (>= 3.2.0, < 7.0)
GIT
remote: https://github.com/openfoodfoundation/ofn-qz.git
revision: 467f6ea1c44529c7c91cac4c8211bbd863588c0b
branch: ofn-rails-4
specs:
ofn-qz (0.1.0)
GIT
remote: https://github.com/openfoodfoundation/select2-rails.git
@@ -26,6 +33,9 @@ PATH
remote: engines/dfc_provider
specs:
dfc_provider (0.0.1)
active_model_serializers (~> 0.8.4)
jwt (~> 2.2)
rspec (~> 3.9)
PATH
remote: engines/order_management
@@ -41,73 +51,66 @@ GEM
remote: https://rubygems.org/
specs:
Ascii85 (1.1.0)
actioncable (7.0.6)
actionpack (= 7.0.6)
activesupport (= 7.0.6)
actioncable (6.1.7)
actionpack (= 6.1.7)
activesupport (= 6.1.7)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.6)
actionpack (= 7.0.6)
activejob (= 7.0.6)
activerecord (= 7.0.6)
activestorage (= 7.0.6)
activesupport (= 7.0.6)
actionmailbox (6.1.7)
actionpack (= 6.1.7)
activejob (= 6.1.7)
activerecord (= 6.1.7)
activestorage (= 6.1.7)
activesupport (= 6.1.7)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.6)
actionpack (= 7.0.6)
actionview (= 7.0.6)
activejob (= 7.0.6)
activesupport (= 7.0.6)
actionmailer (6.1.7)
actionpack (= 6.1.7)
actionview (= 6.1.7)
activejob (= 6.1.7)
activesupport (= 6.1.7)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.6)
actionview (= 7.0.6)
activesupport (= 7.0.6)
rack (~> 2.0, >= 2.2.4)
actionpack (6.1.7)
actionview (= 6.1.7)
activesupport (= 6.1.7)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionpack-action_caching (1.2.2)
actionpack (>= 4.0.0)
actiontext (7.0.6)
actionpack (= 7.0.6)
activerecord (= 7.0.6)
activestorage (= 7.0.6)
activesupport (= 7.0.6)
globalid (>= 0.6.0)
actiontext (6.1.7)
actionpack (= 6.1.7)
activerecord (= 6.1.7)
activestorage (= 6.1.7)
activesupport (= 6.1.7)
nokogiri (>= 1.8.5)
actionview (7.0.6)
activesupport (= 7.0.6)
actionview (6.1.7)
activesupport (= 6.1.7)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_model_serializers (0.8.4)
activemodel (>= 3.0)
active_storage_validations (1.0.4)
active_storage_validations (1.0.3)
activejob (>= 5.2.0)
activemodel (>= 5.2.0)
activestorage (>= 5.2.0)
activesupport (>= 5.2.0)
activejob (7.0.6)
activesupport (= 7.0.6)
activejob (6.1.7)
activesupport (= 6.1.7)
globalid (>= 0.3.6)
activemerchant (1.123.0)
activesupport (>= 4.2)
builder (>= 2.1.2, < 4.0.0)
i18n (>= 0.6.9)
nokogiri (~> 1.4)
activemodel (7.0.6)
activesupport (= 7.0.6)
activerecord (7.0.6)
activemodel (= 7.0.6)
activesupport (= 7.0.6)
activemodel (6.1.7)
activesupport (= 6.1.7)
activerecord (6.1.7)
activemodel (= 6.1.7)
activesupport (= 6.1.7)
activerecord-import (1.4.1)
activerecord (>= 4.2)
activerecord-postgresql-adapter (0.0.1)
@@ -118,23 +121,24 @@ GEM
multi_json (~> 1.11, >= 1.11.2)
rack (>= 2.0.8, < 3)
railties (>= 5.2.4.1)
activestorage (7.0.6)
actionpack (= 7.0.6)
activejob (= 7.0.6)
activerecord (= 7.0.6)
activesupport (= 7.0.6)
activestorage (6.1.7)
actionpack (= 6.1.7)
activejob (= 6.1.7)
activerecord (= 6.1.7)
activesupport (= 6.1.7)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.6)
activesupport (6.1.7)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
acts-as-taggable-on (9.0.1)
activerecord (>= 6.0, < 7.1)
zeitwerk (~> 2.3)
acts-as-taggable-on (8.1.0)
activerecord (>= 5.0, < 6.2)
acts_as_list (1.0.4)
activerecord (>= 4.2)
addressable (2.8.4)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
aes_key_wrap (1.1.0)
afm (0.2.2)
@@ -154,42 +158,43 @@ GEM
awesome_nested_set (3.5.0)
activerecord (>= 4.0.0, < 7.1)
aws-eventstream (1.2.0)
aws-partitions (1.792.0)
aws-sdk-core (3.179.0)
aws-partitions (1.669.0)
aws-sdk-core (3.168.2)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.71.0)
aws-sdk-core (~> 3, >= 3.177.0)
aws-sdk-kms (1.60.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.132.0)
aws-sdk-core (~> 3, >= 3.179.0)
aws-sdk-s3 (1.117.2)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.6)
aws-sigv4 (1.6.0)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.2)
aws-eventstream (~> 1, >= 1.0.2)
base64 (0.1.1)
bcrypt (3.1.18)
bigdecimal (3.0.2)
bindata (2.4.15)
bindata (2.4.12)
bindex (0.8.1)
bootsnap (1.16.0)
bootsnap (1.15.0)
msgpack (~> 1.2)
bugsnag (6.26.0)
bugsnag (6.25.0)
concurrent-ruby (~> 1.0)
builder (3.2.4)
bullet (7.0.7)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
cable_ready (5.0.1)
cable_ready (5.0.0.pre9)
actioncable (>= 5.2)
actionpack (>= 5.2)
actionview (>= 5.2)
activerecord (>= 5.2)
activesupport (>= 5.2)
railties (>= 5.2)
thread-local (>= 1.1.0)
cancancan (1.15.0)
capybara (3.39.2)
capybara (3.38.0)
addressable
matrix
mini_mime (>= 0.1.3)
@@ -213,11 +218,11 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
combine_pdf (1.0.23)
combine_pdf (1.0.22)
matrix
ruby-rc4 (>= 0.1.5)
concurrent-ruby (1.2.2)
connection_pool (2.4.1)
concurrent-ruby (1.1.10)
connection_pool (2.3.0)
crack (0.4.5)
rexml
crass (1.0.6)
@@ -226,20 +231,21 @@ GEM
cuprite (0.14.3)
capybara (~> 3.0)
ferrum (~> 0.13.0)
database_cleaner (2.0.2)
database_cleaner-active_record (>= 2, < 3)
database_cleaner-active_record (2.1.0)
database_cleaner (2.0.1)
database_cleaner-active_record (~> 2.0.0)
database_cleaner-active_record (2.0.0)
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
datafoodconsortium-connector (1.0.0.pre.alpha.8)
virtual_assembly-semantizer (~> 1.0, >= 1.0.5)
date (3.3.3)
debug (1.8.0)
ddtrace (0.54.1)
debase-ruby_core_source (= 0.10.12)
msgpack
debase-ruby_core_source (0.10.12)
debug (1.7.1)
irb (>= 1.5.0)
reline (>= 0.3.1)
debugger-linecache (1.2.0)
devise (4.9.2)
devise (4.8.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
@@ -247,8 +253,8 @@ GEM
warden (~> 1.2.3)
devise-encryptable (0.2.0)
devise (>= 2.1.0)
devise-i18n (1.11.0)
devise (>= 4.9.0)
devise-i18n (1.10.2)
devise (>= 4.8.0)
devise-token_authenticatable (1.1.0)
devise (>= 4.0.0, < 5.0.0)
diff-lcs (1.5.0)
@@ -258,7 +264,7 @@ GEM
dotenv-rails (2.8.1)
dotenv (= 2.8.1)
railties (>= 3.2)
erubi (1.12.0)
erubi (1.11.0)
et-orbi (1.2.7)
tzinfo
excon (0.81.0)
@@ -268,12 +274,12 @@ GEM
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faraday (2.7.10)
faraday (2.6.0)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-follow_redirects (0.3.0)
faraday (>= 1, < 3)
faraday-net_http (3.0.2)
faraday-net_http (3.0.1)
ferrum (0.13)
addressable (~> 2.5)
concurrent-ruby (~> 1.1)
@@ -281,17 +287,15 @@ GEM
websocket-driver (>= 0.6, < 0.8)
ffaker (2.21.0)
ffi (1.15.5)
flipper (0.26.2)
concurrent-ruby (< 2)
flipper-active_record (0.26.2)
activerecord (>= 4.2, < 8)
flipper (~> 0.26.2)
flipper-ui (0.26.2)
flipper (0.20.4)
flipper-active_record (0.20.4)
activerecord (>= 5.0, < 7)
flipper (~> 0.20.4)
flipper-ui (0.20.4)
erubi (>= 1.0.0, < 2.0.0)
flipper (~> 0.26.2)
flipper (~> 0.20.4)
rack (>= 1.4, < 3)
rack-protection (>= 1.5.3, <= 4.0.0)
sanitize (< 7)
rack-protection (>= 1.5.3, < 2.2.0)
fog-aws (2.0.1)
fog-core (~> 1.38)
fog-json (~> 1.0)
@@ -309,14 +313,18 @@ GEM
nokogiri (>= 1.5.11, < 2.0.0)
foreman (0.87.2)
formatador (0.2.5)
fugit (1.8.1)
fugit (1.7.1)
et-orbi (~> 1, >= 1.2.7)
raabro (~> 1.4)
fuubar (2.5.1)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
geocoder (1.8.2)
globalid (1.1.0)
geocoder (1.8.1)
gitlab-omniauth-openid-connect (0.10.0)
addressable (~> 2.7)
omniauth (>= 1.9, < 3)
openid_connect (~> 1.2)
globalid (1.0.0)
activesupport (>= 5.0)
gmaps4rails (2.1.2)
good_migrations (0.2.1)
@@ -332,7 +340,7 @@ GEM
hiredis (0.6.3)
htmlentities (4.3.4)
httpclient (2.8.3)
i18n (1.14.1)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
i18n-js (3.9.2)
i18n (>= 0.6.6)
@@ -343,7 +351,7 @@ GEM
activerecord (>= 3.0)
io-console (0.6.0)
ipaddress (0.8.3)
irb (1.6.4)
irb (1.6.2)
reline (>= 0.3.0)
jmespath (1.6.2)
jquery-rails (4.4.0)
@@ -353,20 +361,12 @@ GEM
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
json (2.6.3)
json-canonicalization (0.3.2)
json-jwt (1.16.3)
json-jwt (1.16.0)
activesupport (>= 4.2)
aes_key_wrap
bindata
faraday (~> 2.0)
faraday-follow_redirects
json-ld (3.2.5)
htmlentities (~> 4.3)
json-canonicalization (~> 0.3, >= 0.3.2)
link_header (~> 0.0, >= 0.0.8)
multi_json (~> 1.15)
rack (>= 2.2, < 4)
rdf (~> 3.2, >= 3.2.10)
json-schema (3.0.0)
addressable (>= 2.8)
json_spec (1.1.5)
@@ -374,59 +374,51 @@ GEM
rspec (>= 2.0, < 4.0)
jsonapi-serializer (2.2.0)
activesupport (>= 4.2)
jwt (2.7.1)
knapsack_pro (5.3.5)
jwt (2.6.0)
knapsack_pro (3.7.0)
rake
language_server-protocol (3.17.0.3)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.8.1)
launchy (>= 2.2, < 3)
link_header (0.0.8)
listen (3.8.0)
libv8-node (15.14.0.1)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.21.3)
loofah (2.19.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.8.1)
nokogiri (>= 1.5.9)
mail (2.7.1)
mini_mime (>= 0.1.1)
net-imap
net-pop
net-smtp
marcel (1.0.2)
matrix (0.4.2)
method_source (1.0.0)
mime-types (3.5.0)
mime-types (3.4.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2023.0218.1)
mime-types-data (3.2021.0225)
mimemagic (0.4.3)
nokogiri (~> 1)
rake
mini_magick (4.11.0)
mini_mime (1.1.2)
mini_portile2 (2.8.4)
minitest (5.18.1)
mini_portile2 (2.8.1)
mini_racer (0.4.0)
libv8-node (~> 15.14.0.0)
minitest (5.17.0)
monetize (1.12.0)
money (~> 6.12)
money (6.16.0)
i18n (>= 0.6.4, <= 2)
msgpack (1.7.1)
msgpack (1.6.0)
multi_json (1.15.0)
multi_xml (0.6.0)
net-imap (0.3.6)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.1)
net-protocol (0.1.3)
timeout
net-smtp (0.3.3)
net-smtp (0.3.2)
net-protocol
newrelic_rpm (9.3.1)
nio4r (2.5.9)
nokogiri (1.15.3)
mini_portile2 (~> 2.8.2)
nio4r (2.5.8)
nokogiri (1.13.10)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
oauth2 (1.4.11)
faraday (>= 0.17.3, < 3.0)
@@ -434,16 +426,13 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 4)
omniauth (2.1.1)
omniauth (2.1.0)
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth_openid_connect (0.6.1)
omniauth (>= 1.9, < 3)
openid_connect (~> 1.1)
openid_connect (1.4.2)
activemodel
attr_required (>= 1.0.0)
@@ -458,15 +447,14 @@ GEM
orm_adapter (0.5.0)
pagy (5.10.1)
activesupport
paper_trail (12.3.0)
paper_trail (12.1.0)
activerecord (>= 5.2)
request_store (~> 1.1)
parallel (1.23.0)
paranoia (2.6.2)
parallel (1.22.1)
paranoia (2.6.1)
activerecord (>= 5.1, < 7.1)
parser (3.2.2.3)
parser (3.1.3.0)
ast (~> 2.4.1)
racc
paypal-sdk-core (0.3.4)
multi_json (~> 1.0)
xml-simple
@@ -479,19 +467,16 @@ GEM
ruby-rc4
ttfunk
pg (1.2.3)
private_address_check (0.5.0)
power_assert (2.0.2)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (5.0.3)
puma (6.3.0)
public_suffix (5.0.0)
puma (5.6.5)
nio4r (~> 2.0)
query_count (1.1.1)
activerecord (>= 4.2)
railties (>= 4.2)
raabro (1.4.0)
racc (1.7.1)
rack (2.2.7)
racc (1.6.1)
rack (2.2.4)
rack-mini-profiler (2.3.4)
rack (>= 1.2.0)
rack-oauth2 (1.21.3)
@@ -500,79 +485,75 @@ GEM
httpclient
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
rack-protection (3.0.5)
rack-protection (2.1.0)
rack
rack-proxy (0.7.6)
rack-proxy (0.7.0)
rack
rack-rewrite (1.5.1)
rack-test (2.1.0)
rack-ssl (1.4.1)
rack
rack-test (2.0.2)
rack (>= 1.3)
rack-timeout (0.6.3)
rails (7.0.6)
actioncable (= 7.0.6)
actionmailbox (= 7.0.6)
actionmailer (= 7.0.6)
actionpack (= 7.0.6)
actiontext (= 7.0.6)
actionview (= 7.0.6)
activejob (= 7.0.6)
activemodel (= 7.0.6)
activerecord (= 7.0.6)
activestorage (= 7.0.6)
activesupport (= 7.0.6)
rails (6.1.7)
actioncable (= 6.1.7)
actionmailbox (= 6.1.7)
actionmailer (= 6.1.7)
actionpack (= 6.1.7)
actiontext (= 6.1.7)
actionview (= 6.1.7)
activejob (= 6.1.7)
activemodel (= 6.1.7)
activerecord (= 6.1.7)
activestorage (= 6.1.7)
activesupport (= 6.1.7)
bundler (>= 1.15.0)
railties (= 7.0.6)
railties (= 6.1.7)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
activesupport (>= 5.0.1.rc1)
rails-dom-testing (2.1.1)
activesupport (>= 5.0.0)
minitest
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-erd (1.7.2)
activerecord (>= 4.2)
activesupport (>= 4.2)
choice (~> 0.2.0)
ruby-graphviz (~> 1.2)
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
rails-i18n (7.0.7)
rails-html-sanitizer (1.4.4)
loofah (~> 2.19, >= 2.19.1)
rails-i18n (7.0.6)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
rails_safe_tasks (1.0.0)
railties (7.0.6)
actionpack (= 7.0.6)
activesupport (= 7.0.6)
railties (6.1.7)
actionpack (= 6.1.7)
activesupport (= 6.1.7)
method_source
rake (>= 12.2)
thor (~> 1.0)
zeitwerk (~> 2.5)
rainbow (3.1.1)
rake (13.0.6)
ransack (2.6.0)
activerecord (>= 6.0.4)
activesupport (>= 6.0.4)
ransack (2.4.2)
activerecord (>= 5.2.4)
activesupport (>= 5.2.4)
i18n
rb-fsevent (0.11.2)
rb-fsevent (0.11.0)
rb-inotify (0.10.1)
ffi (~> 1.0)
rdf (3.2.11)
link_header (~> 0.0, >= 0.0.8)
redcarpet (3.6.0)
redis (4.8.1)
redis-client (0.14.1)
connection_pool
regexp_parser (2.8.1)
reline (0.3.3)
redcarpet (3.5.1)
redis (4.8.0)
regexp_parser (2.6.1)
reline (0.3.2)
io-console (~> 0.5)
request_store (1.5.1)
request_store (1.5.0)
rack (>= 1.4)
responders (3.1.0)
actionpack (>= 5.2)
railties (>= 5.2)
rexml (3.2.6)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rexml (3.2.5)
roadie (5.0.1)
css_parser (~> 1.4)
nokogiri (~> 1.8)
@@ -582,63 +563,61 @@ GEM
rodf (1.2.0)
builder (>= 3.0)
rubyzip (>= 1.0)
roo (2.10.0)
roo (2.9.0)
nokogiri (~> 1)
rubyzip (>= 1.3.0, < 3.0.0)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.2)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.3)
rspec (3.10.0)
rspec-core (~> 3.10.0)
rspec-expectations (~> 3.10.0)
rspec-mocks (~> 3.10.0)
rspec-core (3.10.2)
rspec-support (~> 3.10.0)
rspec-expectations (3.10.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.5)
rspec-support (~> 3.10.0)
rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-rails (6.0.3)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
rspec-core (~> 3.12)
rspec-expectations (~> 3.12)
rspec-mocks (~> 3.12)
rspec-support (~> 3.12)
rspec-support (~> 3.10.0)
rspec-rails (5.1.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
railties (>= 5.2)
rspec-core (~> 3.10)
rspec-expectations (~> 3.10)
rspec-mocks (~> 3.10)
rspec-support (~> 3.10)
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.12.1)
rswag-api (2.10.1)
rspec-support (3.10.3)
rswag-api (2.8.0)
railties (>= 3.1, < 7.1)
rswag-specs (2.10.1)
rswag-specs (2.8.0)
activesupport (>= 3.1, < 7.1)
json-schema (>= 2.2, < 4.0)
railties (>= 3.1, < 7.1)
rspec-core (>= 2.14)
rswag-ui (2.10.1)
rswag-ui (2.8.0)
actionpack (>= 3.1, < 7.1)
railties (>= 3.1, < 7.1)
rubocop (1.56.0)
base64 (~> 0.1.1)
rubocop (1.42.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.3)
parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.1, < 2.0)
rubocop-ast (>= 1.24.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.29.0)
parser (>= 3.2.1.0)
rubocop-rails (2.20.2)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.24.1)
parser (>= 3.1.1.0)
rubocop-rails (2.17.4)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
ruby-graphviz (1.2.5)
rexml
ruby-progressbar (1.13.0)
ruby-progressbar (1.11.0)
ruby-rc4 (0.1.5)
ruby-vips (2.1.4)
ffi (~> 1.12)
@@ -646,9 +625,6 @@ GEM
rubyzip (2.3.2)
rufus-scheduler (3.8.2)
fugit (~> 1.1, >= 1.1.6)
sanitize (6.0.2)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
sass (3.4.25)
sass-rails (5.0.8)
railties (>= 5.2.0)
@@ -660,14 +636,14 @@ GEM
semantic_range (3.0.0)
shoulda-matchers (5.3.0)
activesupport (>= 5.2.0)
sidekiq (7.1.2)
concurrent-ruby (< 2)
connection_pool (>= 2.3.0)
rack (>= 2.2.4)
redis-client (>= 0.14.0)
sidekiq-scheduler (5.0.3)
sidekiq (6.5.8)
connection_pool (>= 2.2.5, < 3)
rack (~> 2.0)
redis (>= 4.5.0, < 5)
sidekiq-scheduler (4.0.3)
redis (>= 4.2.0)
rufus-scheduler (~> 3.2)
sidekiq (>= 6, < 8)
sidekiq (>= 4, < 7)
tilt (>= 1.4.0)
simplecov (0.22.0)
docile (~> 1.1)
@@ -678,7 +654,7 @@ GEM
spreadsheet_architect (5.0.0)
caxlsx (>= 3.3.0, < 4)
rodf (>= 1.0.0, < 2)
spring (4.1.1)
spring (4.1.0)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
sprockets (3.7.2)
@@ -688,41 +664,43 @@ GEM
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
state_machines (0.6.0)
state_machines-activemodel (0.9.0)
activemodel (>= 6.0)
state_machines (>= 0.6.0)
state_machines-activerecord (0.9.0)
activerecord (>= 6.0)
state_machines-activemodel (>= 0.9.0)
stimulus_reflex (3.5.0.rc3)
actioncable (>= 5.2, < 8)
actionpack (>= 5.2, < 8)
actionview (>= 5.2, < 8)
activesupport (>= 5.2, < 8)
cable_ready (~> 5.0)
nokogiri (~> 1.0)
rack (>= 2, < 4)
railties (>= 5.2, < 8)
redis (>= 4.0, < 6.0)
stringex (2.8.6)
stripe (8.6.0)
state_machines (0.5.0)
state_machines-activemodel (0.8.0)
activemodel (>= 5.1)
state_machines (>= 0.5.0)
state_machines-activerecord (0.8.0)
activerecord (>= 5.1)
state_machines-activemodel (>= 0.8.0)
stimulus_reflex (3.5.0.pre9)
actioncable (>= 5.2)
actionpack (>= 5.2)
actionview (>= 5.2)
activesupport (>= 5.2)
cable_ready (>= 5.0.0.pre9)
nokogiri
rack
railties (>= 5.2)
redis
stringex (2.8.5)
stripe (8.0.0)
swd (1.3.0)
activesupport (>= 3)
attr_required (>= 0.0.5)
httpclient (>= 2.4)
temple (0.8.2)
thor (1.2.2)
test-unit (3.5.7)
power_assert
thor (1.2.1)
thread-local (1.1.0)
tilt (2.1.0)
tilt (2.0.11)
timecop (0.9.6)
timeout (0.4.0)
timeout (0.3.0)
ttfunk (1.7.0)
tzinfo (2.0.6)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
unicode-display_width (2.4.2)
unicode-display_width (2.3.0)
uniform_notifier (1.16.0)
valid_email2 (4.0.6)
valid_email2 (4.0.4)
activemodel (>= 3.2)
mail (~> 2.5)
validate_email (0.1.6)
@@ -731,8 +709,8 @@ GEM
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
vcr (6.2.0)
view_component (3.5.0)
vcr (6.1.0)
view_component (2.80.0)
activesupport (>= 5.2.0, < 8.0)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
@@ -740,8 +718,6 @@ GEM
rails (>= 5.2, < 8.0)
stimulus_reflex (>= 3.5.0.pre2)
view_component (>= 2.28.0)
virtual_assembly-semantizer (1.0.5)
json-ld (~> 3.2, >= 3.2.3)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.2.0)
@@ -756,7 +732,7 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (5.4.4)
webpacker (5.4.3)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 5.2)
@@ -773,7 +749,7 @@ GEM
xml-simple (1.1.8)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.8)
zeitwerk (2.6.6)
PLATFORMS
ruby
@@ -786,7 +762,7 @@ DEPENDENCIES
activerecord-import
activerecord-postgresql-adapter
activerecord-session_store
acts-as-taggable-on
acts-as-taggable-on (~> 8.1)
acts_as_list (= 1.0.4)
angular-rails-templates (>= 0.3.0)
angular_rails_csrf
@@ -799,7 +775,7 @@ DEPENDENCIES
bootsnap
bugsnag
bullet
cable_ready (= 5.0.1)
cable_ready (= 5.0.0.pre9)
cancancan (~> 1.15.0)
capybara
catalog!
@@ -807,8 +783,8 @@ DEPENDENCIES
combine_pdf
cuprite
database_cleaner
datafoodconsortium-connector
db2fog!
ddtrace
debug (>= 1.0.0)
debugger-linecache
devise
@@ -819,7 +795,6 @@ DEPENDENCIES
digest
dotenv-rails
factory_bot_rails (= 6.2.0)
faraday
ffaker
flipper
flipper-active_record
@@ -828,6 +803,7 @@ DEPENDENCIES
foreman
fuubar (~> 2.5.1)
geocoder
gitlab-omniauth-openid-connect
gmaps4rails
good_migrations
haml
@@ -849,32 +825,31 @@ DEPENDENCIES
mime-types
mimemagic (> 0.3.5)
mini_portile2 (~> 2.8)
mini_racer (= 0.4.0)
monetize (~> 1.11)
newrelic_rpm
oauth2 (~> 1.4.7)
ofn-qz!
omniauth-rails_csrf_protection
omniauth_openid_connect
openid_connect (~> 1.3)
order_management!
pagy (~> 5.1)
paper_trail (~> 12.1)
paper_trail (~> 12.1.0)
paranoia (~> 2.4)
paypal-sdk-merchant (= 1.117.2)
pdf-reader
pg (~> 1.2.3)
private_address_check
pry (~> 0.13.0)
puma
query_count
rack-mini-profiler (< 3.0.0)
rack-rewrite
rack-ssl
rack-timeout
rails
rails (>= 6.1.4)
rails-controller-testing
rails-erd
rails-i18n
rails_safe_tasks (~> 1.0)
ransack (~> 2.6.0)
ransack (= 2.4.2)
redcarpet
redis (>= 4.0)
responders
@@ -898,9 +873,10 @@ DEPENDENCIES
spring
spring-commands-rspec
state_machines-activerecord
stimulus_reflex (= 3.5.0.rc3)
stimulus_reflex (= 3.5.0.pre9)
stringex (~> 2.8.5)
stripe
test-unit (~> 3.5)
timecop
valid_email2
vcr
@@ -915,7 +891,7 @@ DEPENDENCIES
wkhtmltopdf-binary
RUBY VERSION
ruby 3.1.4p223
ruby 3.0.3p157
BUNDLED WITH
2.4.3
2.1.4

View File

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

View File

@@ -33,7 +33,7 @@ We also have a [Super Admin Guide][super-admin-guide] to help with configuration
## Testing
If you'd like to help out with testing, please introduce yourself on the #testing channel on [Slack][slack-invite] and download the [ZenHub browser extension][zenhub] to view the development pipeline. Also, do have a look in our [Welcome New QAs board][welcome-qa] for some good first issues, both on manual and automated testing (RSpec/Capybara).
If you'd like to help out with testing, please introduce yourself on the #testing channel on [Slack][slack-invite] and download the [ZenHub browser extension][zenhub] to view the development pipeline. Also, do have a look in our [Welcome New QAs board](https://github.com/orgs/openfoodfoundation/projects/1) for some good first issues, both on manual and automated testing (RSpec/Capybara).
We use [BrowserStack](https://www.browserstack.com/) as a manual testing tool. BrowserStack provides open source projects with unlimited and free of charge accounts. A big thanks to them!
@@ -52,6 +52,5 @@ Copyright (c) 2012 - 2022 Open Food Foundation, released under the AGPL licence.
[ofn-handbook]: https://ofn-user-guide.gitbook.io/ofn-handbook/
[ofn-install]: https://github.com/openfoodfoundation/ofn-install
[super-admin-guide]: https://ofn-user-guide.gitbook.io/ofn-super-admin-guide
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/5
[welcome-qa]: https://github.com/orgs/openfoodfoundation/projects/6
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/2
[zenhub]: https://www.zenhub.com/extension

View File

@@ -7,3 +7,4 @@
require_relative 'config/application'
Openfoodnetwork::Application.load_tasks

View File

@@ -12,6 +12,5 @@ angular.module("ofn.admin", [
"admin.orders"
]).config ($httpProvider, $locationProvider, $qProvider) ->
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
# for the next line, you should also probably check file: app/assets/javascripts/admin/utils/utils.js.coffee
$locationProvider.hashPrefix('')
$qProvider.errorOnUnhandledRejections(false)

View File

@@ -7,6 +7,7 @@
// jquery and angular
//= require jquery2
//= require jquery_ujs
//= require jquery.ui.all
//= require jquery.powertip
//= require jquery.cookie
@@ -68,6 +69,25 @@
//= require textAngular.min.js
//= require i18n/translations
//= require darkswarm/i18n.translate.js
//= require moment/min/moment.min.js
//= require moment/locale/ar.js
//= require moment/locale/ca.js
//= require moment/locale/de.js
//= require moment/locale/en-gb.js
//= require moment/locale/es.js
//= require moment/locale/fil.js
//= require moment/locale/fr.js
//= require moment/locale/it.js
//= require moment/locale/nb.js
//= require moment/locale/nl-be.js
//= require moment/locale/pt-br.js
//= require moment/locale/pt.js
//= require moment/locale/ru.js
//= require moment/locale/sv.js
//= require moment/locale/tr.js
//= require moment/locale/pl.js
//= require js-big-decimal/dist/web/js-big-decimal.min.js
// foundation
//= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js

View File

@@ -113,7 +113,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
(DirtyProducts.count() > 0 and confirm(t("unsaved_changes_confirmation"))) or (DirtyProducts.count() == 0)
editProductUrl = (product, variant) ->
"/admin/products/" + product.id + ((if variant then "/variants/" + variant.id else "")) + "/edit"
"/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit"
$scope.editWarn = (product, variant) ->
if confirm_unsaved_changes()
@@ -135,7 +135,6 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
display_name: null
on_hand: null
price: null
tax_category_id: null
DisplayProperties.setShowVariants product.id, true
@@ -163,7 +162,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
if confirm(t("are_you_sure"))
$http(
method: "DELETE"
url: "/api/v0/products/" + product.id + "/variants/" + variant.id
url: "/api/v0/products/" + product.permalink_live + "/variants/" + variant.id
).then (response) ->
$scope.removeVariant(product, variant)
else
@@ -347,9 +346,15 @@ filterSubmitProducts = (productsToFilter) ->
if product.hasOwnProperty("category_id")
filteredProduct.primary_taxon_id = product.category_id
hasUpdatableProperty = true
if product.hasOwnProperty("tax_category_id")
filteredProduct.tax_category_id = product.tax_category_id
hasUpdatableProperty = true
if product.hasOwnProperty("inherits_properties")
filteredProduct.inherits_properties = product.inherits_properties
hasUpdatableProperty = true
if product.hasOwnProperty("available_on")
filteredProduct.available_on = product.available_on
hasUpdatableProperty = true
if filteredMaster?
filteredProduct.master_attributes = filteredMaster
hasUpdatableProperty = true
@@ -387,9 +392,6 @@ filterSubmitVariant = (variant) ->
if variant.hasOwnProperty("display_name")
filteredVariant.display_name = variant.display_name
hasUpdatableProperty = true
if variant.hasOwnProperty("tax_category_id")
filteredVariant.tax_category_id = variant.tax_category_id
hasUpdatableProperty = true
if variant.hasOwnProperty("display_as")
filteredVariant.display_as = variant.display_as
hasUpdatableProperty = true

View File

@@ -0,0 +1,5 @@
angular.module("admin.dropdown").directive "linksDropdown", ($window)->
restrict: "C"
scope:
links: "="
templateUrl: "admin/links_dropdown.html"

View File

@@ -55,12 +55,28 @@ angular.module("admin.enterprises")
else
alert ("#{manager.email}" + " " + t("is_already_manager"))
$scope.inviteManager = ->
$scope.invite_errors = $scope.invite_success = null
email = $scope.newUser
$http.post("/admin/manager_invitations", {email: email, enterprise_id: $scope.Enterprise.id}).then (response)->
$scope.addManager({id: response.data.user, email: email})
$scope.invite_success = t('user_invited', email: email)
.catch (response) ->
$scope.invite_errors = response.data.errors
$scope.resetModal = ->
$scope.newUser = $scope.invite_errors = $scope.invite_success = null
$scope.removeLogo = ->
$scope.performEnterpriseAction("removeLogo", "immediate_logo_removal_warning", "removed_logo_successfully")
$scope.removePromoImage = ->
$scope.performEnterpriseAction("removePromoImage", "immediate_promo_image_removal_warning", "removed_promo_image_successfully")
$scope.removeTermsAndConditions = ->
$scope.performEnterpriseAction("removeTermsAndConditions", "immediate_terms_and_conditions_removal_warning", "removed_terms_and_conditions_successfully")
$scope.performEnterpriseAction = (enterpriseActionName, warning_message_key, success_message_key) ->
return unless confirm($scope.translation(warning_message_key))

View File

@@ -0,0 +1,32 @@
angular.module("admin.enterprises").directive 'termsAndConditionsWarning', ($rootScope, $compile, $templateCache, DialogDefaults, $timeout) ->
restrict: 'A'
scope: true
link: (scope, element, attr) ->
# This file input click handler will hold the browser file input dialog and show a warning modal
scope.hold_file_input_and_show_warning_modal = (event) ->
event.preventDefault()
scope.template = $compile($templateCache.get('admin/modals/terms_and_conditions_warning.html'))(scope)
if scope.template.dialog
scope.template.dialog(DialogDefaults)
scope.template.dialog('open')
$rootScope.$evalAsync()
element.bind 'click', scope.hold_file_input_and_show_warning_modal
# When the user presses continue in the warning modal, we open the browser file input dialog
scope.continue = ->
scope.template.dialog('close')
$rootScope.$evalAsync()
# unbind warning modal handler and click file input again to open the browser file input dialog
element.unbind('click').trigger('click')
# afterwards, bind warning modal handler again so that the warning is shown the next time
$timeout ->
element.bind 'click', scope.hold_file_input_and_show_warning_modal
return
scope.close = ->
scope.template.dialog('close')
$rootScope.$evalAsync()
return

View File

@@ -11,6 +11,5 @@ angular.module("admin.indexUtils").factory 'SortOptions', ->
sortingExpr
toggle: (predicate) ->
# predicate is a string or an array of strings
@reverse = (JSON.stringify(@predicate) == JSON.stringify(predicate)) && !@reverse
@reverse = (@predicate == predicate) && !@reverse
@predicate = predicate

View File

@@ -9,51 +9,41 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.sharedResource = false
$scope.columns = Columns.columns
$scope.sorting = SortOptions
$scope.sorting.toggle("order_date")
$scope.pagination = LineItems.pagination
$scope.per_page_options = [
{id: 15, name: t('js.admin.orders.index.per_page', results: 15)},
{id: 50, name: t('js.admin.orders.index.per_page', results: 50)},
{id: 100, name: t('js.admin.orders.index.per_page', results: 100)}
]
$scope.page = 1
$scope.per_page = $scope.per_page_options[0].id
$scope.filterByVariantId = null
searchThrough = ["order_distributor_name",
"order_bill_address_phone",
"order_bill_address_firstname",
"order_bill_address_lastname",
"order_bill_address_full_name",
"variant_product_supplier_name",
"order_email",
"order_number",
"product_name"].join("_or_") + "_cont"
$scope.confirmRefresh = ->
LineItems.allSaved() || confirm(t("unsaved_changes_warning"))
$scope.initStartAndEnDate = ->
$scope.startDate = moment().startOf('day').subtract(7, 'days').format('YYYY-MM-DD')
$scope.endDate = moment().startOf('day').format('YYYY-MM-DD')
$scope.resetFilters = ->
$scope.distributorFilter = ''
$scope.supplierFilter = ''
$scope.orderCycleFilter = ''
$scope.query = ''
$scope.startDate = undefined
$scope.endDate = undefined
event = new CustomEvent('flatpickr:clear')
$scope.quickSearch = ''
$scope.initStartAndEnDate()
event = new CustomEvent('flatpickr:change', {
detail: {
startDate: $scope.startDate,
endDate: $scope.endDate
}
})
window.dispatchEvent(event)
$scope.resetSelectFilters = ->
$scope.resetFilters()
$scope.refreshData()
$scope.fetchResults = ->
# creates indirection in order to factorize the code between orders and bulk orders
# used in app/views/admin/shared/_angular_per_page_controls.html.haml
$scope.refreshData()
$scope.refreshData = ->
$scope.formattedStartDate = moment($scope.startDate).format()
$scope.formattedEndDate = moment($scope.endDate).add(1,'day').format()
return unless moment($scope.formattedStartDate).isValid() and moment($scope.formattedEndDate).isValid()
return "cancel" unless $scope.confirmRefresh()
$scope.loadOrders()
$scope.loadLineItems()
unless $scope.initialized
@@ -61,52 +51,57 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.dereferenceLoadedData()
$scope.loadOrders = ->
return $scope.orders = [] unless $scope.line_items.length
$scope.setOrderCycleDateRange = ->
start_date = OrderCycles.byID[$scope.orderCycleFilter].orders_open_at
end_date = OrderCycles.byID[$scope.orderCycleFilter].orders_close_at
format = "YYYY-MM-DD HH:mm:ss Z"
$scope.startDate = moment(start_date, format).format('YYYY-MM-DD')
$scope.endDate = moment(end_date, format).startOf('day').format('YYYY-MM-DD')
# throw a flatpickr:change event to change the date back in the datepicker
event = new CustomEvent('flatpickr:change', {
detail: {
startDate: $scope.startDate,
endDate: $scope.endDate
}
})
window.dispatchEvent(event)
$scope.loadOrders = ->
RequestMonitor.load $scope.orders = Orders.index(
"q[id_in][]": $scope.line_items.map((line_item) -> line_item.order.id)
"q[state_not_eq]": "canceled",
"q[shipment_state_not_eq]": "shipped",
"q[completed_at_not_null]": "true",
"q[distributor_id_eq]": $scope.distributorFilter,
"q[order_cycle_id_eq]": $scope.orderCycleFilter,
"q[completed_at_gteq]": $scope.formattedStartDate,
"q[completed_at_lt]": $scope.formattedEndDate
)
$scope.loadLineItems = ->
[formattedStartDate, formattedEndDate] = $scope.formatDates($scope.startDate, $scope.endDate)
RequestMonitor.load LineItems.index(
"q[#{searchThrough}]": $scope.query,
"q[variant_id_eq]": $scope.filterByVariantId if $scope.filterByVariantId,
"q[order_state_not_eq]": "canceled",
"q[order_shipment_state_not_eq]": "shipped",
"q[order_completed_at_not_null]": "true",
"q[order_distributor_id_eq]": $scope.distributorFilter,
"q[variant_product_supplier_id_eq]": $scope.supplierFilter,
"q[order_order_cycle_id_eq]": $scope.orderCycleFilter,
"q[order_completed_at_gteq]": if formattedStartDate then formattedStartDate else undefined,
"q[order_completed_at_lt]": if formattedEndDate then formattedEndDate else undefined,
"q[s]": "order_completed_at desc",
"page": $scope.page,
"per_page": $scope.per_page
"q[order_completed_at_gteq]": $scope.formattedStartDate,
"q[order_completed_at_lt]": $scope.formattedEndDate
)
$scope.formatDates = (startDate, endDate) ->
formattedStartDate = moment(startDate).format('YYYY-MM-DD') if startDate
formattedEndDate = moment(endDate).add(1,'day').format('YYYY-MM-DD') if endDate
return [formattedStartDate, formattedEndDate]
$scope.loadAssociatedData = ->
RequestMonitor.load $scope.distributors = Enterprises.index(action: "visible", ams_prefix: "basic", "q[sells_in][]": ["own", "any"])
RequestMonitor.load $scope.orderCycles = OrderCycles.index(ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{moment().subtract(90,'days').format()}")
RequestMonitor.load $scope.suppliers = Enterprises.index(action: "visible", ams_prefix: "basic", "q[is_primary_producer_eq]": "true")
$scope.dereferenceLoadedData = ->
RequestMonitor.load $q.all([$scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.line_items.$promise]).then ->
RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.line_items.$promise]).then ->
Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID
Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.byID
Dereferencer.dereferenceAttr $scope.line_items, "supplier", Enterprises.byID
$scope.loadOrders()
RequestMonitor.load $q.all([$scope.orders.$promise]).then ->
Dereferencer.dereferenceAttr $scope.line_items, "order", Orders.byID
Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID
Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.byID
$scope.bulk_order_form.$setPristine()
StatusMessage.clear()
Dereferencer.dereferenceAttr $scope.line_items, "order", Orders.byID
$scope.bulk_order_form.$setPristine()
StatusMessage.clear()
unless $scope.initialized
$scope.initialized = true
@@ -126,16 +121,16 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
else
StatusMessage.display 'failure', t "unsaved_changes_error"
$scope.cancelOrder = (order, sendEmailCancellation, restock_items) ->
$scope.cancelOrder = (order, sendEmailCancellation) ->
return $http(
method: 'GET'
url: "/admin/orders/#{order.number}/fire?e=cancel&send_cancellation_email=#{sendEmailCancellation}&restock_items=#{restock_items}")
url: "/admin/orders/#{order.number}/fire?e=cancel&send_cancellation_email=#{sendEmailCancellation}")
$scope.deleteLineItem = (lineItem) ->
if lineItem.order.item_count == 1
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) ->
ofnCancelOrderAlert((confirm, sendEmailCancellation) ->
if confirm
$scope.cancelOrder(lineItem.order, sendEmailCancellation, restock_items).then(->
$scope.cancelOrder(lineItem.order, sendEmailCancellation).then(->
$scope.refreshData()
)
else
@@ -157,18 +152,14 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
willCancelOrders = true if (order.item_count == itemsPerOrder.get(order).length)
if willCancelOrders
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) ->
ofnCancelOrderAlert((confirm, sendEmailCancellation) ->
if confirm
itemsPerOrder.forEach (items, order) =>
if order.item_count == items.length
$scope.cancelOrder(order, sendEmailCancellation, restock_items).then(-> $scope.refreshData())
$scope.cancelOrder(order, sendEmailCancellation).then(-> $scope.refreshData())
else
Promise.all(LineItems.delete(item) for item in items).then(-> $scope.refreshData())
, "js.admin.deleting_item_will_cancel_order")
else
ofnDeleteLineItemsAlert(() ->
Promise.all(LineItems.delete(item) for item in lineItemsToDelete).then(-> $scope.refreshData())
, lineItemsToDelete.length)
$scope.allBoxesChecked = ->
checkedCount = $scope.filteredLineItems.reduce (count,lineItem) ->
@@ -183,17 +174,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.setSelectedUnitsVariant = (unitsProduct,unitsVariant) ->
$scope.selectedUnitsProduct = unitsProduct
$scope.selectedUnitsVariant = unitsVariant
$scope.filterByVariantId = unitsVariant.id
$scope.page = 1
$scope.refreshData()
$scope.resetSelectedUnitsVariant = ->
$scope.selectedUnitsProduct = { }
$scope.selectedUnitsVariant = { }
$scope.filterByVariantId = null
$scope.sharedResource = false
$scope.page = 1
$scope.refreshData()
$scope.getLineItemScale = (lineItem) ->
if lineItem.units_product && lineItem.units_variant && (lineItem.units_product.variant_unit == "weight" || lineItem.units_product.variant_unit == "volume")
@@ -239,7 +219,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.getGroupBySizeFormattedValueWithUnitName = (value, unitsProduct, unitsVariant) ->
scale = $scope.getScale(unitsProduct, unitsVariant)
if scale && value
if scale
value = value / scale if scale != 28.35 && scale != 1 && scale != 453.6 # divide by scale if not smallest unit
$scope.getFormattedValueWithUnitName(value, unitsProduct, unitsVariant, scale)
else
@@ -283,8 +263,4 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
lineItem.final_weight_volume = LineItems.pristineByID[lineItem.id].final_weight_volume * lineItem.quantity / LineItems.pristineByID[lineItem.id].quantity
$scope.weightAdjustedPrice(lineItem)
$scope.changePage = (newPage) ->
$scope.page = newPage
$scope.refreshData()
$scope.resetSelectFilters()

View File

@@ -0,0 +1,6 @@
angular.module("admin.lineItems").filter "variantFilter", ->
return (lineItems,selectedUnitsProduct,selectedUnitsVariant,sharedResource) ->
filtered = []
filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedUnitsProduct,{}) ||
(lineItem.units_product.id == selectedUnitsProduct.id && (sharedResource || lineItem.units_variant.id == selectedUnitsVariant.id ) ) )
filtered

View File

@@ -18,6 +18,7 @@ angular.module('admin.orderCycles')
$scope.submit = ($event, destination) ->
$event.preventDefault()
NavigationCheck.clear()
StatusMessage.display 'progress', t('js.saving')
OrderCycle.update(destination, $scope.order_cycle_form)

View File

@@ -35,11 +35,7 @@ angular.module('admin.orderCycles')
OrderCycle.removeExchangeFee(exchange, index)
$scope.order_cycle_form.$dirty = true
$scope.setPickupTimeFieldDirty = (index, pickup_time) ->
# if the pickup_time is already set we are in edit mode, so no need to set pickup_time field as dirty
# to show it is required (it has a red border when set to dirty)
return if pickup_time
$scope.setPickupTimeFieldDirty = (index) ->
$timeout ->
pickup_time_field_name = "order_cycle_outgoing_exchange_" + index + "_pickup_time"
$scope.order_cycle_form[pickup_time_field_name].$setDirty()

View File

@@ -33,11 +33,9 @@ angular.module("admin.orderCycles").controller "OrderCyclesCtrl", ($scope, $q, C
StatusMessage.display 'notice', "You have unsaved changes" if newVal
$scope.showMore = (days) ->
orderCycles = OrderCycles.index(ams_prefix: "index",
"q[orders_close_at_gt]": "#{daysFromToday($scope.ordersCloseAtLimit - days)}",
"q[orders_close_at_lteq]": "#{daysFromToday($scope.ordersCloseAtLimit)}"
)
$scope.ordersCloseAtLimit -= days
existingIDs = Object.keys(OrderCycles.byID)
orderCycles = OrderCycles.index(ams_prefix: "index", "q[orders_close_at_gt]": "#{daysFromToday($scope.ordersCloseAtLimit)}", "q[id_not_in][]": existingIDs)
orderCycles.$promise.then ->
$scope.orderCycles.push(orderCycle) for orderCycle in orderCycles
compileData()

View File

@@ -33,4 +33,6 @@ angular.module('admin.orderCycles').factory('Enterprise', ($resource) ->
variantsOf: (product) ->
if product.variants.length > 0
variant.id for variant in product.variants
else
[product.master_id]
})

View File

@@ -0,0 +1,14 @@
angular.module("admin.orders").controller "bulkCancelCtrl", ($scope, $http, $timeout) ->
$scope.cancelOrder = (orderIds, sendEmailCancellation, restock_items) ->
$http(
method: 'post'
url: "/admin/orders/bulk_cancel?order_ids=#{orderIds}&send_cancellation_email=#{sendEmailCancellation}&restock_items=#{restock_items}" ).then(->
window.location.reload()
)
$scope.cancelSelectedOrders = ->
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) ->
if confirm
$scope.cancelOrder $scope.selected_orders, sendEmailCancellation, restock_items
)

View File

@@ -0,0 +1,32 @@
angular.module("admin.orders").controller "bulkInvoiceCtrl", ($scope, $http, $timeout) ->
$scope.createBulkInvoice = ->
$scope.invoice_id = null
$scope.poll = 1
$scope.loading = true
$scope.message = null
$scope.error = null
$scope.poll_wait = 5 # 5 Seconds between each check
$scope.poll_retries = 80 # Maximum checks before stopping
$http.post('/admin/orders/invoices', {order_ids: $scope.selected_orders}).then (response) ->
$scope.invoice_id = response.data
$scope.pollBulkInvoice()
$scope.pollBulkInvoice = ->
$timeout($scope.nextPoll, $scope.poll_wait * 1000)
$scope.nextPoll = ->
$http.get('/admin/orders/invoices/'+$scope.invoice_id+'/poll').then (response) ->
$scope.loading = false
$scope.message = t('js.admin.orders.index.bulk_invoice_created')
.catch (response) ->
$scope.poll++
if $scope.poll > $scope.poll_retries
$scope.loading = false
$scope.error = t('js.admin.orders.index.bulk_invoice_failed')
return
$scope.pollBulkInvoice()

View File

@@ -0,0 +1,117 @@
angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, RequestMonitor, Orders, SortOptions, $window, $filter, $location, KeyValueMapStore) ->
$scope.RequestMonitor = RequestMonitor
$scope.pagination = Orders.pagination
$scope.orders = Orders.all
$scope.sortOptions = SortOptions
$scope.per_page_options = [
{id: 15, name: t('js.admin.orders.index.per_page', results: 15)},
{id: 50, name: t('js.admin.orders.index.per_page', results: 50)},
{id: 100, name: t('js.admin.orders.index.per_page', results: 100)}
]
$scope.selected_orders = []
$scope.checkboxes = {}
$scope.selected = false
$scope.select_all = false
$scope.poll = 0
$scope.rowStatus = {}
KeyValueMapStore.localStorageKey = 'ordersFilters'
KeyValueMapStore.storableKeys = ["q", "sorting", "page", "per_page"]
$scope.initialise = ->
unless KeyValueMapStore.restoreValues($scope)
$scope.setDefaults()
$scope.fetchResults()
$scope.setDefaults = ->
$scope.per_page = 15
$scope.q = {
completed_at_not_null: true
}
e = new CustomEvent("flatpickr_clear");
window.dispatchEvent(e)
$scope.clearFilters = () ->
KeyValueMapStore.clearKeyValueMap()
$scope.setDefaults()
$scope.fetchResults()
$scope.fetchResults = (page=1) ->
startDateWithTime = $scope.appendStringIfNotEmpty($scope.q?.completed_at_gteq, ' 00:00:00')
endDateWithTime = $scope.appendStringIfNotEmpty($scope.q?.completed_at_lteq, ' 23:59:59')
$scope.resetSelected()
params = {
'q[completed_at_gteq]': startDateWithTime,
'q[completed_at_lteq]': endDateWithTime,
'q[state_eq]': $scope.q?.state_eq,
'q[number_cont]': $scope.q?.number_cont,
'q[email_cont]': $scope.q?.email_cont,
'q[bill_address_firstname_start]': $scope.q?.bill_address_firstname_start,
'q[bill_address_lastname_start]': $scope.q?.bill_address_lastname_start,
# Set default checkbox values to null. See: https://github.com/openfoodfoundation/openfoodnetwork/pull/3076#issuecomment-440010498
'q[completed_at_not_null]': $scope.q?.completed_at_not_null || null,
'q[distributor_id_in][]': $scope.q?.distributor_id_in,
'q[order_cycle_id_in][]': $scope.q?.order_cycle_id_in,
'q[s]': $scope.sorting || 'completed_at desc',
shipping_method_id: $scope.q?.shipping_method_id,
per_page: $scope.per_page,
page: page
}
KeyValueMapStore.setStoredValues($scope)
RequestMonitor.load(Orders.index(params).$promise)
$scope.appendStringIfNotEmpty = (baseString, stringToAppend) ->
return baseString unless baseString
return baseString if baseString.endsWith(stringToAppend)
baseString + stringToAppend
$scope.resetSelected = ->
$scope.selected_orders.length = 0
$scope.selected = false
$scope.select_all = false
$scope.checkboxes = {}
$scope.toggleSelection = (id) ->
index = $scope.selected_orders.indexOf(id)
if index == -1
$scope.selected_orders.push(id)
else
$scope.selected_orders.splice(index, 1)
$scope.toggleAll = ->
$scope.selected_orders.length = 0
$scope.orders.forEach (order) ->
$scope.checkboxes[order.id] = $scope.select_all
$scope.selected_orders.push order.id if $scope.select_all
$scope.$watch 'sortOptions', (sort) ->
return unless sort && sort.predicate != ""
$scope.sorting = sort.getSortingExpr()
$scope.fetchResults()
, true
$scope.capturePayment = (order) ->
$scope.rowAction('capture', order)
$scope.shipOrder = (order) ->
$scope.rowAction('ship', order)
$scope.rowAction = (action, order) ->
$scope.rowStatus[order.id] = "loading"
Orders[action](order).$promise.then (data) ->
$scope.rowStatus[order.id] = "success"
$timeout(->
$scope.rowStatus[order.id] = null
, 1500)
, (error) ->
$scope.rowStatus[order.id] = "error"
$scope.changePage = (newPage) ->
$scope.page = newPage
$scope.fetchResults(newPage)

View File

@@ -0,0 +1,5 @@
angular.module("admin.orders").directive "invoicesModal", ($modal) ->
restrict: 'C'
link: (scope, elem, attrs, ctrl) ->
elem.on "click", (ev) =>
scope.uploadModal = $modal.open(templateUrl: 'admin/modals/bulk_invoice.html', controller: ctrl, scope: scope, windowClass: 'simple-modal')

View File

@@ -14,4 +14,7 @@ angular.module("admin.resources").factory 'EnterpriseResource', ($resource) ->
'removePromoImage':
url: '/api/v0/enterprises/:id/promo_image.json'
method: 'DELETE'
'removeTermsAndConditions':
url: '/api/v0/enterprises/:id/terms_and_conditions.json'
method: 'DELETE'
})

View File

@@ -63,3 +63,4 @@ angular.module("admin.resources").factory 'Enterprises', ($q, $filter, Enterpris
removeLogo: performActionOnEnterpriseResource(EnterpriseResource.removeLogo)
removePromoImage: performActionOnEnterpriseResource(EnterpriseResource.removePromoImage)
removeTermsAndConditions: performActionOnEnterpriseResource(EnterpriseResource.removeTermsAndConditions)

View File

@@ -0,0 +1,15 @@
$ ->
($ '#new_image_link').click (event) ->
event.preventDefault()
($ '.no-objects-found').hide()
($ this).hide()
$.ajax
type: 'GET'
url: @href
data: (
authenticity_token: AUTH_TOKEN
)
success: (r) ->
($ '#images').html r

View File

@@ -0,0 +1,7 @@
($ '#cancel_link').click (event) ->
event.preventDefault()
($ '.no-objects-found').show()
($ '#new_image_link').show()
($ '#images').html('')

View File

@@ -15,7 +15,7 @@ $(document).ready(function() {
console.log(msg);
});
}
$('.admin-order-edit-form a.ship').click(handle_ship_click);
$('[data-hook=admin_order_edit_form] a.ship').click(handle_ship_click);
//handle shipping method edit click
$('a.edit-method').click(toggleMethodEdit);
@@ -37,7 +37,7 @@ $(document).ready(function() {
console.log(msg);
});
}
$('.admin-order-edit-form a.save-method').click(handle_shipping_method_save);
$('[data-hook=admin_order_edit_form] a.save-method').click(handle_shipping_method_save);
//handle tracking info edit/delete
@@ -64,8 +64,8 @@ $(document).ready(function() {
return Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipmentNumber + ".json");
}
$('.admin-order-edit-form a.save-tracking').click(saveTrackingInfo);
$('.admin-order-edit-form a.delete-tracking').click(deleteTrackingInfo);
$('[data-hook=admin_order_edit_form] a.save-tracking').click(saveTrackingInfo);
$('[data-hook=admin_order_edit_form] a.delete-tracking').click(deleteTrackingInfo);
// handle note edit/delete
@@ -96,8 +96,8 @@ $(document).ready(function() {
});
}
$('.admin-order-edit-form a.save-note').click(saveNote);
$('.admin-order-edit-form a.delete-note').click(deleteNote);
$('[data-hook=admin_order_edit_form] a.save-note').click(saveNote);
$('[data-hook=admin_order_edit_form] a.delete-note').click(deleteNote);
// Makes API call for notes/tracking info
makeApiCall = function(url, params) {

View File

@@ -4,8 +4,7 @@ $(document).ready(function() {
initAlert()
initConfirm()
initButtonCancel()
initLinkCancel()
initCancelOrder()
if ($('#variant_autocomplete_template').length > 0) {
window.variantTemplate = Handlebars.compile($('#variant_autocomplete_template').text());
@@ -100,7 +99,6 @@ adjustItems = function(shipment_number, variant_id, quantity, restock_item){
doAdjustItems(shipment_number, variant_id, quantity, inventory_units, restock_item, () => {
var redirectTo = new URL(Spree.routes.cancel_order.toString());
redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
redirectTo.searchParams.append("restock_item", restock_item);
window.location.href = redirectTo.toString();
});
}
@@ -252,18 +250,6 @@ ofnCancelOrderAlert = function(callback, i18nKey) {
$('#custom-confirm').show();
}
ofnDeleteLineItemsAlert = function(callback, count) {
$('#custom-confirm .message').html(`${t("js.admin.orders.delete_line_items_html", {count: count})}`);
$('#custom-confirm button.confirm').click(() => {
$('#custom-confirm').hide();
callback();
});
$('#custom-confirm button.cancel').click(() => {
$('#custom-confirm').hide();
});
$('#custom-confirm').show();
}
ofnConfirm = function(callback) {
$('#custom-confirm .message').html(
` ${t("are_you_sure")}
@@ -278,23 +264,17 @@ ofnConfirm = function(callback) {
$('#custom-confirm').show();
}
initCancelAction = function(e){
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) => {
if (confirm) {
var redirectTo = new URL(Spree.routes.cancel_order.toString());
redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
redirectTo.searchParams.append("restock_items", restock_items);
window.location.href = redirectTo.toString();
}
initCancelOrder = function() {
$('#cancel_order_form').submit(function(e){
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) => {
if (confirm) {
var redirectTo = new URL(Spree.routes.cancel_order.toString());
redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
redirectTo.searchParams.append("restock_items", restock_items);
window.location.href = redirectTo.toString();
}
});
e.preventDefault();
return false;
});
e.preventDefault();
return false;
};
initButtonCancel = function() {
$('#cancel_order_form').submit(initCancelAction)
}
initLinkCancel = function() {
$('#links-dropdown a[href$="cancel"]').click(initCancelAction);
}

View File

@@ -8,7 +8,7 @@ handle_move = (e, data) ->
node = data.rslt.o
new_parent = data.rslt.np
url = new URL(Spree.routes.admin_taxonomy_taxons)
url = new URL(base_url)
url.pathname = url.pathname + '/' + node.attr("id")
data = {
_method: "put",

View File

@@ -10,12 +10,6 @@ angular.module("admin.utils").directive "variantAutocomplete", ($timeout) ->
element.select2
placeholder: t('admin.orders.select_variant')
minimumInputLength: 3
formatInputTooShort: ->
t('admin.select2.minimal_search_length', count: 3)
formatSearching: ->
t('admin.select2.searching')
formatNoMatches: ->
t('admin.select2.no_matches')
ajax:
url: Spree.routes.variants_search
datatype: "json"

View File

@@ -5,8 +5,8 @@ angular.module("admin.utils").factory "StatusMessage", ->
alert: {style: {color: 'grey'}}
notice: {style: {color: 'grey'}}
success: {style: {color: '#9fc820'}}
failure: {style: {color: '#C85136'}}
error: {style: {color: '#C85136'}}
failure: {style: {color: '#da5354'}}
error: {style: {color: '#da5354'}}
statusMessage:
text: ""

View File

@@ -1,4 +1,2 @@
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider, $locationProvider) ->
# for the next line, you should also probably check file: app/assets/javascripts/admin/admin_ofn.js.coffee
$locationProvider.hashPrefix('')
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -1,15 +0,0 @@
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery2
//= require admin/spree/spree-select2
//= require admin/spree/handlebar_extensions
//= require i18n/translations
//= require darkswarm/i18n.translate.js
window.angular = { module: function(noop){ return { value: function(){} } } }

View File

@@ -29,6 +29,24 @@
#
#= require angular-flash.min.js
#
#= require moment/min/moment.min.js
#= require moment/locale/ar.js
#= require moment/locale/ca.js
#= require moment/locale/de.js
#= require moment/locale/en-gb.js
#= require moment/locale/es.js
#= require moment/locale/fil.js
#= require moment/locale/fr.js
#= require moment/locale/it.js
#= require moment/locale/nb.js
#= require moment/locale/nl-be.js
#= require moment/locale/pt-br.js
#= require moment/locale/pt.js
#= require moment/locale/ru.js
#= require moment/locale/sv.js
#= require moment/locale/tr.js
#= require moment/locale/pl.js
#
#= require modernizr
#
#= require foundation-sites/js/foundation.js
@@ -49,3 +67,11 @@ document.addEventListener "turbo:before-render", ->
rootscope = null
window.injector = null
true
document.addEventListener "ajax:beforeSend", (event) =>
window.Turbo.navigator.adapter.progressBar.setValue(0)
window.Turbo.navigator.adapter.progressBar.show()
document.addEventListener "ajax:complete", (event) =>
window.Turbo.navigator.adapter.progressBar.setValue(100)
window.Turbo.navigator.adapter.progressBar.hide()

View File

@@ -0,0 +1,22 @@
angular.module('Darkswarm').controller "AccordionCtrl", ($scope, localStorageService, $timeout, $document, CurrentHub) ->
$scope.accordionSections = ["details", "billing", "shipping", "payment"]
$scope.accordion = { details: true, billing: true, shipping: true, payment: true }
$scope.show = (section) ->
$scope.accordion[section] = true
$scope.scrollTo = (section) ->
# Scrolling is confused by our position:fixed top bar - add an offset to scroll
# to the correct location, plus 5px buffer
offset_height = $("nav.top-bar").height() + 5
$document.scrollTo($("##{section}"), offset_height, 400)
$scope.$on 'purchaseFormInvalid', (event, form) ->
# Scroll to first invalid section
for section in $scope.accordionSections
if not form[section].$valid
$scope.show section
$timeout ->
$scope.scrollTo(section)
, 50
break

View File

@@ -0,0 +1,12 @@
angular.module('Darkswarm').controller "BillingCtrl", ($scope, $timeout, $controller) ->
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
$scope.name = "billing"
$scope.nextPanel = "shipping"
$scope.summary = ->
[$scope.order.bill_address.address1,
$scope.order.bill_address.city,
$scope.order.bill_address.zipcode]
$timeout $scope.onTimeout

View File

@@ -0,0 +1,43 @@
angular.module('Darkswarm').controller "CheckoutCtrl", ($scope, localStorageService, Checkout, CurrentUser, CurrentHub, $http) ->
$scope.Checkout = Checkout
$scope.submitted = false
# Bind to local storage
$scope.fieldsToBind = ["bill_address", "email", "payment_method_id", "shipping_method_id", "ship_address"]
prefix = "order_#{Checkout.order.id}#{CurrentUser.id or ""}#{CurrentHub.hub.id}"
for field in $scope.fieldsToBind
localStorageService.bind $scope, "Checkout.order.#{field}", Checkout.order[field], "#{prefix}_#{field}"
localStorageService.bind $scope, "Checkout.ship_address_same_as_billing", true, "#{prefix}_sameasbilling"
localStorageService.bind $scope, "Checkout.default_bill_address", false, "#{prefix}_defaultasbilladdress"
localStorageService.bind $scope, "Checkout.default_ship_address", false, "#{prefix}_defaultasshipaddress"
$scope.order = Checkout.order # Ordering is important
$scope.secrets = Checkout.secrets
$scope.enabled = !!CurrentUser.id?
$scope.purchase = (event, form) ->
event.preventDefault()
$scope.formdata = form
$scope.submitted = true
if CurrentUser.id
$scope.validateForm(form)
else
$scope.ensureUserIsGuest()
$scope.validateForm = ->
if $scope.formdata.$valid
$scope.Checkout.purchase()
else
$scope.$broadcast 'purchaseFormInvalid', $scope.formdata
$scope.ensureUserIsGuest = (callback = null) ->
$http.post("/user/registered_email", {email: $scope.order.email})
.then (response)->
window.CableReady.perform(response.data)
.catch ->
$scope.validateForm() if $scope.submitted
callback() if callback

View File

@@ -0,0 +1,8 @@
angular.module('Darkswarm').controller "CountryCtrl", ($scope, availableCountries) ->
$scope.countries = availableCountries
$scope.countriesById = $scope.countries.reduce (obj, country) ->
obj[country.id] = country
obj
, {}

View File

@@ -0,0 +1,24 @@
angular.module('Darkswarm').controller "DetailsCtrl", ($scope, $timeout, $http, CurrentUser, SpreeUser, $controller) ->
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
$scope.name = "details"
$scope.nextPanel = "billing"
$scope.login_or_next = (event) ->
event.preventDefault()
unless CurrentUser.id
$scope.ensureUserIsGuest($scope.next)
return
$scope.next()
$scope.summary = ->
[$scope.fullName(),
$scope.order.email,
$scope.order.bill_address.phone]
$scope.fullName = ->
[$scope.order.bill_address.firstname ? null,
$scope.order.bill_address.lastname ? null].join(" ").trim()
$timeout $scope.onTimeout

View File

@@ -0,0 +1,19 @@
angular.module('Darkswarm').controller "PaymentCtrl", ($scope, $timeout, savedCreditCards, Dates, $controller) ->
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
$scope.savedCreditCards = savedCreditCards
$scope.name = "payment"
$scope.months = Dates.months
$scope.years = Dates.years
$scope.secrets.card_month = "1"
$scope.secrets.card_year = moment().year()
for card in savedCreditCards when card.is_default
$scope.secrets.selected_card = card.id
break
$scope.summary = ->
[$scope.Checkout.paymentMethod()?.name]
$timeout $scope.onTimeout

View File

@@ -0,0 +1,11 @@
angular.module('Darkswarm').controller "ShippingCtrl", ($scope, $timeout, ShippingMethods, $controller) ->
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
$scope.ShippingMethods = ShippingMethods
$scope.name = "shipping"
$scope.nextPanel = "payment"
$scope.summary = ->
[$scope.Checkout.shippingMethod()?.name]
$timeout $scope.onTimeout

View File

@@ -25,8 +25,6 @@ angular.module('Darkswarm').controller "OrderCycleChangeCtrl", ($scope, $rootSco
Cart.reloadFinalisedLineItems()
ChangeableOrdersAlert.reload()
$rootScope.$broadcast 'orderCycleSelected'
event = new CustomEvent('orderCycleSelected')
window.dispatchEvent(event)
$scope.closesInLessThan3Months = () ->
moment().diff(moment(OrderCycle.orders_close_at(), "YYYY-MM-DD HH:mm:SS Z"), 'days') > -75

View File

@@ -0,0 +1,22 @@
angular.module('Darkswarm').controller "PageSelectionCtrl", ($scope, $rootScope, $location) ->
$scope.selectedPage = ->
# The path looks like `/contact` for the URL `https://ofn.org/shop#/contact`.
# We remove the slash at the beginning.
page = $location.path()[1..]
return $scope.whitelist[0] unless page
# If the path points to an unrelated path like `/login`, stay where we were.
return $scope.lastPage unless page in $scope.whitelist
$scope.lastPage = page
page
$scope.whitelistPages = (pages) ->
$scope.whitelist = pages
$scope.lastPage = pages[0]
# when an order cycle is changed, ensure the shop tab is active to save a click
$rootScope.$on "orderCycleSelected", ->
if $scope.selectedPage() != "shop"
$location.path("shop")

View File

@@ -7,11 +7,8 @@ angular.module('Darkswarm').controller "RegistrationFormCtrl", ($scope, Registra
form.$valid
$scope.create = (form) ->
if ($scope.valid(form))
$scope.disableButton()
EnterpriseRegistrationService.create($scope.enableButton).then(() ->
$scope.enableButton()
)
$scope.disableButton()
EnterpriseRegistrationService.create($scope.enableButton) if $scope.valid(form)
$scope.update = (nextStep, form) ->
EnterpriseRegistrationService.update(nextStep) if $scope.valid(form)

View File

@@ -17,12 +17,9 @@ angular.module('Darkswarm').directive "ofnFlash", (flash, $timeout, RailsFlashLo
# Callback when a new flash message is pushed to flash service
show = (message, type)=>
return unless message
# if same message already exists, don't add it again
return if $scope.flashes.some((flash) -> flash.message == message)
$scope.flashes.push({message: message, type: typePairings[type]})
$timeout($scope.delete, 10000)
if message
$scope.flashes.push({message: message, type: typePairings[type]})
$timeout($scope.delete, 10000)
$scope.delete = ->
$scope.flashes.shift()

View File

@@ -1,4 +1,4 @@
angular.module('Darkswarm').directive "ofnOnHand", (StockQuantity, Messages) ->
angular.module('Darkswarm').directive "ofnOnHand", (StockQuantity) ->
restrict: 'A'
require: "ngModel"
scope: true
@@ -16,7 +16,7 @@ angular.module('Darkswarm').directive "ofnOnHand", (StockQuantity, Messages) ->
ngModel.$parsers.push (viewValue) ->
available_quantity = scope.available_quantity()
if parseInt(viewValue) > available_quantity
Messages.flash({error: t("js.insufficient_stock", {on_hand: available_quantity})})
alert t("js.insufficient_stock", {on_hand: available_quantity})
viewValue = available_quantity
ngModel.$setViewValue viewValue
ngModel.$render()

View File

@@ -2,7 +2,6 @@ angular.module('Darkswarm').factory "EnterpriseModal", ($modal, $rootScope, $htt
# Build a modal popup for an enterprise.
new class EnterpriseModal
open: (enterprise)->
return if enterprise.visible == 'hidden'
scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise
scope.embedded_layout = window.location.search.indexOf("embedded_shopfront=true") != -1

View File

@@ -55,10 +55,6 @@ angular.module('Darkswarm').factory "EnterpriseRegistrationService", ($http, Reg
).catch((response) ->
Loading.clear()
alert(t('failed_to_update_enterprise_unknown'))
if response.data.errors.instagram
igErr = document.querySelector("#instagram-error")
igErr.style.display = 'block'
igErr.textContent = response.data.errors.instagram[0]
)
prepare: =>

View File

@@ -33,9 +33,9 @@ angular.module('Darkswarm').factory 'Products', (OrderCycleResource, OrderCycle,
prices = (v.price for v in product.variants)
product.price = Math.min.apply(null, prices)
product.hasVariants = product.variants?.length > 0
product.primaryImage = product.image?.small_url if product.image
product.primaryImage = product.images[0]?.small_url if product.images
product.primaryImageOrMissing = product.primaryImage || "/noimage/small.png"
product.largeImage = product.image?.large_url if product.image
product.largeImage = product.images[0]?.large_url if product.images
dereference: ->
for product in @fetched_products

View File

@@ -0,0 +1,5 @@
$( document ).ready(function() {
$("#closeie").click(function() {
$("#ie-warning").hide();
});
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,11 @@
.ofn-drop-down.ofn-drop-down-v2.right#columns-dropdown{ ng: { controller: 'ColumnsDropdownCtrl' } }
.ofn-drop-down-label
= "&nbsp; #{t('admin.columns')}".html_safe
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
.ofn-drop-down.right#columns-dropdown{ ng: { controller: 'ColumnsDropdownCtrl' } }
%span{ :class => 'icon-reorder' }= "&nbsp; #{t('admin.columns')}".html_safe
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
%div.menu{ 'ng-show' => "expanded" }
.menu_items
.menu_item{ ng: { repeat: "column in columns", click: "toggle(column);" } }
%input.redesigned-input{ type: "checkbox", ng: { checked: "column.visible" } }
{{ column.name }}
%hr
%div.menu_item.text-center
%input.fullwidth.orange{ type: "button", ng: { value: "saved() ? 'Saved': 'Saving'", show: "saved() || saving", disabled: "saved()" } }
%input.fullwidth.red{ type: "button", :value => t('admin.column_save_as_default').html_safe, ng: { show: "!saved() && !saving", click: "saveColumnPreferences(action)"} }
%div.menu_item{ ng: { repeat: "column in columns", click: "toggle(column)", class: "{selected: column.visible}" } }
%span.check
%span.name {{ column.name }}
%hr
%div.menu_item.text-center
%input.fullwidth.orange{ type: "button", ng: { value: "saved() ? 'Saved': 'Saving'", show: "saved() || saving", disabled: "saved()" } }
%input.fullwidth.red{ type: "button", :value => t('admin.column_save_as_default').html_safe, ng: { show: "!saved() && !saving", click: "saveColumnPreferences(action)"} }

View File

@@ -0,0 +1,10 @@
.ofn-drop-down
%span
%i.icon-check
{{ 'admin.actions' | t }}
%i{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
%div.menu{ 'ng-show' => "expanded" }
%a.menu_item{ 'ng-repeat' => "link in links", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method || 'get' }}", confirm: "{{link.confirm}}" } }
%span
%i{ ng: { class: "link.icon" } }
%span {{ link.name }}

View File

@@ -0,0 +1,14 @@
%div
.margin-bottom-30.text-center
.text-big
{{ 'js.admin.modals.terms_and_conditions_warning.title' | t }}
.margin-bottom-30
%p
{{ 'js.admin.modals.terms_and_conditions_warning.message_1' | t }}
.margin-bottom-30
%p
{{ 'js.admin.modals.terms_and_conditions_warning.message_2' | t }}
.text-center
%input.button.red{ type: 'button', value: t('js.admin.modals.close'), ng: { click: 'close()' } }
%input.button.red{ type: 'button', value: t('js.admin.modals.continue'), ng: { click: 'continue()' } }

View File

@@ -3,7 +3,7 @@
.seven.columns.alpha
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage == ''", style: 'StatusMessage.statusMessage.style' } }
{{ StatusMessage.statusMessage.text || "&nbsp;" }}
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage !== ''" }, style: 'color: #C85136' }
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage !== ''" }, style: 'color: #da5354' }
{{ StatusMessage.invalidMessage || "&nbsp;" }}
.nine.columns.omega.text-right{ ng: { transclude: true } }

View File

@@ -15,6 +15,6 @@
.columns.small-12.medium-6.large-6.product-img
%img{"ng-src" => "{{::product.largeImage}}", "ng-if" => "::product.largeImage"}
%img.placeholder{ src: Spree::Image.default_image_url(:large), "ng-if" => "::!product.largeImage"}
%img.placeholder{ src: "/noimage/large.png", "ng-if" => "::!product.largeImage"}
%ng-include{src: "'partials/close.html'"}

View File

@@ -1,13 +0,0 @@
# frozen_string_literal: true
class ScopedChannel < ApplicationCable::Channel
class << self
def for_id(id)
"ScopedChannel:#{id}"
end
end
def subscribed
stream_from "ScopedChannel:#{params[:id]}"
end
end

View File

@@ -1,13 +0,0 @@
# frozen_string_literal: true
class SessionChannel < ApplicationCable::Channel
def self.for_request(request)
"SessionChannel:#{request.session.id}"
end
def subscribed
return reject if current_user.nil?
stream_from "SessionChannel:#{session_id}"
end
end

View File

@@ -1,19 +0,0 @@
# frozen_string_literal: true
class ConfirmModalComponent < ModalComponent
def initialize(id:, confirm_actions: nil, reflex: nil, controller: nil, message: nil,
confirm_reflexes: nil)
super(id: id, close_button: true)
@confirm_actions = confirm_actions
@reflex = reflex
@confirm_reflexes = confirm_reflexes
@controller = controller
@message = message
end
private
def close_button_class
"secondary"
end
end

View File

@@ -1,10 +0,0 @@
%div{ id: @id, "data-controller": "modal #{@controller}", "data-action": "keyup@document->modal#closeIfEscapeKey", "data-#{@controller}-reflex-value": @reflex }
.reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" }
.reveal-modal.fade.tiny.help-modal{ "data-modal-target": "modal" }
= content
= render @message if @message
.modal-actions
%input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.cancel'), "data-action": "click->modal#close" }
%input{ class: "button icon-plus primary", type: 'button', value: t('js.admin.modals.confirm'), "data-action": @confirm_actions, "data-reflex": @confirm_reflexes }

View File

@@ -1,4 +0,0 @@
.modal-actions {
display: flex;
justify-content: space-around;
}

View File

@@ -1,7 +1,26 @@
# frozen_string_literal: true
class HelpModalComponent < ModalComponent
class HelpModalComponent < ViewComponent::Base
def initialize(id:, close_button: true)
super(id: id, close_button: close_button)
@id = id
@close_button = close_button
end
private
def close_button_class
if namespace == "admin"
"red"
else
"primary"
end
end
def close_button?
!!@close_button
end
def namespace
helpers.controller_path.split("/").first
end
end

View File

@@ -1,26 +0,0 @@
# frozen_string_literal: true
class ModalComponent < ViewComponent::Base
def initialize(id:, close_button: true)
@id = id
@close_button = close_button
end
private
def close_button_class
if namespace == "admin"
"red"
else
"primary"
end
end
def close_button?
!!@close_button
end
def namespace
helpers.controller_path.split("/").first
end
end

View File

@@ -16,7 +16,7 @@ nav {
&.inactive {
cursor: default;
color: $dark-grey;
color: $disabled-dark;
}
}
@@ -52,7 +52,7 @@ nav {
}
.pagination-gap {
color: $dark-grey;
color: $disabled-dark;
}
.pagination-page {

View File

@@ -1,26 +1,18 @@
# frozen_string_literal: true
class ProductComponent < ViewComponentReflex::Component
DATETIME_FORMAT = '%F %T'
def initialize(product:, columns:)
super
@product = product
@image = @product.image if product.image.present?
@columns = columns.map do |c|
@image = @product.images[0] if product.images.any?
@columns = columns.map { |c|
{
id: c[:value],
value: column_value(c[:value])
}
end
}
end
# This must be define when using ProductComponent.with_collection()
def collection_key
@product.id
end
# rubocop:disable Metrics/CyclomaticComplexity
def column_value(column)
case column
when 'name'
@@ -28,30 +20,11 @@ class ProductComponent < ViewComponentReflex::Component
when 'price'
@product.price
when 'unit'
"#{@product.variants.first.unit_value} #{@product.variant_unit}"
"#{@product.unit_value} #{@product.variant_unit}"
when 'producer'
@product.supplier.name
when 'category'
@product.taxons.map(&:name).join(', ')
when 'sku'
@product.sku
when 'on_hand'
@product.on_hand || 0
when 'on_demand'
@product.on_demand
when 'tax_category'
@product.variant.tax_category.name
when 'inherits_properties'
@product.inherits_properties
when 'import_date'
format_date(@product.import_date)
end
end
# rubocop:enable Metrics/CyclomaticComplexity
private
def format_date(date)
date&.strftime(DATETIME_FORMAT) || ''
end
end

View File

@@ -1,6 +1,6 @@
%tr
- @columns.each do |column|
%td.products_column{class: column[:id]}
- if column[:id] == "name" && @image&.attachment.present?
- if column[:id] == "name" && @image
= image_tag @image.url(:mini)
= column[:value]

View File

@@ -3,36 +3,27 @@
class ProductsTableComponent < ViewComponentReflex::Component
include Pagy::Backend
SORTABLE_COLUMNS = ['name', 'import_date'].freeze
SELECTABLE_COLUMNS = [
{ label: I18n.t("admin.products_page.columns_selector.price"), value: "price" },
{ label: I18n.t("admin.products_page.columns_selector.unit"), value: "unit" },
{ label: I18n.t("admin.products_page.columns_selector.producer"), value: "producer" },
{ label: I18n.t("admin.products_page.columns_selector.category"), value: "category" },
{ label: I18n.t("admin.products_page.columns_selector.sku"), value: "sku" },
{ label: I18n.t("admin.products_page.columns_selector.on_hand"), value: "on_hand" },
{ label: I18n.t("admin.products_page.columns_selector.on_demand"), value: "on_demand" },
{ label: I18n.t("admin.products_page.columns_selector.tax_category"), value: "tax_category" },
{
label: I18n.t("admin.products_page.columns_selector.inherits_properties"),
value: "inherits_properties"
},
{ label: I18n.t("admin.products_page.columns_selector.import_date"), value: "import_date" }
].sort do |a, b|
SORTABLE_COLUMNS = ["name"].freeze
SELECTABLE_COMUMNS = [{ label: I18n.t("admin.products_page.columns_selector.price"),
value: "price" },
{ label: I18n.t("admin.products_page.columns_selector.unit"),
value: "unit" },
{ label: I18n.t("admin.products_page.columns_selector.producer"),
value: "producer" },
{ label: I18n.t("admin.products_page.columns_selector.category"),
value: "category" }].sort { |a, b|
a[:label] <=> b[:label]
end.freeze
}.freeze
PER_PAGE_VALUE = [10, 25, 50, 100].freeze
PER_PAGE = PER_PAGE_VALUE.map { |value| { label: value, value: value } }
NAME_COLUMN = {
label: I18n.t("admin.products_page.columns.name"), value: "name", sortable: true
}.freeze
NAME_COLUMN = { label: I18n.t("admin.products_page.columns.name"), value: "name",
sortable: true }.freeze
def initialize(user:)
super
@user = user
@selectable_columns = SELECTABLE_COLUMNS
@columns_selected = ['unit', 'price', 'on_hand', 'category', 'import_date']
@selectable_columns = SELECTABLE_COMUMNS
@columns_selected = ["price", "unit"]
@per_page = PER_PAGE
@per_page_selected = [10]
@categories = [{ label: "All", value: "all" }] +
@@ -49,20 +40,16 @@ class ProductsTableComponent < ViewComponentReflex::Component
@search_term = ""
end
# any change on a "reflex_data_attributes" (defined in the template) will trigger a re render
def before_render
fetch_products
refresh_columns
end
# Element refers to the component the data is set on
def search_term
# Element is SearchInputComponent
@search_term = element.dataset['value']
end
def toggle_column
# Element is SelectorComponent
column = element.dataset['value']
@columns_selected = if @columns_selected.include?(column)
@columns_selected - [column]
@@ -72,33 +59,26 @@ class ProductsTableComponent < ViewComponentReflex::Component
end
def click_sort
# Element is TableHeaderComponent
@sort = {
column: element.dataset['sort-value'],
direction: element.dataset['sort-direction'] == "asc" ? "desc" : "asc"
}
@sort = { column: element.dataset['sort-value'],
direction: element.dataset['sort-direction'] == "asc" ? "desc" : "asc" }
end
def toggle_per_page
# Element is SelectorComponent
selected = element.dataset['value'].to_i
@per_page_selected = [selected] if PER_PAGE_VALUE.include?(selected)
end
def toggle_category
# Element is SelectorWithFilterComponent
category_clicked = element.dataset['value']
@categories_selected = toggle_selector_with_filter(category_clicked, @categories_selected)
end
def toggle_producer
# Element is SelectorWithFilterComponent
producer_clicked = element.dataset['value']
@producers_selected = toggle_selector_with_filter(producer_clicked, @producers_selected)
end
def change_page
# Element is PaginationComponent
page = element.dataset['page'].to_i
@page = page if page > 0
end
@@ -106,13 +86,10 @@ class ProductsTableComponent < ViewComponentReflex::Component
private
def refresh_columns
@columns = @columns_selected.map do |column|
{
label: I18n.t("admin.products_page.columns.#{column}"),
value: column,
sortable: SORTABLE_COLUMNS.include?(column)
}
end.sort! { |a, b| a[:label] <=> b[:label] }
@columns = @columns_selected.map { |column|
{ label: I18n.t("admin.products_page.columns.#{column}"), value: column,
sortable: SORTABLE_COLUMNS.include?(column) }
}.sort! { |a, b| a[:label] <=> b[:label] }
@columns.unshift(NAME_COLUMN)
end
@@ -167,13 +144,9 @@ class ProductsTableComponent < ViewComponentReflex::Component
def product_query_includes
[
:image,
variants: [
:default_price,
:stock_locations,
:stock_items,
:variant_overrides
]
master: [:images],
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides,
{ option_values: :option_type }]
]
end
end

View File

@@ -1,5 +1,5 @@
.search-input {
border: 1px solid $light-grey;
border: 1px solid $disabled-light;
height: 3em;
display: flex;
line-height: 3em;

View File

@@ -2,7 +2,7 @@
position: relative;
.selector-main {
border: 1px solid $light-grey;
border: 1px solid $disabled-light;
height: 3em;
position: relative;
cursor: pointer;
@@ -30,7 +30,7 @@
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid $light-grey;
border-top: 5px solid $disabled-light;
}
}
}
@@ -42,7 +42,7 @@
z-index: 1;
background-color: white;
margin-top: -1px;
border: 1px solid $light-grey;
border: 1px solid $disabled-light;
.selector-items {
overflow-y: auto;
@@ -51,7 +51,7 @@
.selector-item {
padding-left: 10px;
padding-right: 10px;
border-bottom: 1px solid $light-grey;
border-bottom: 1px solid $disabled-light;
position: relative;
height: 3em;
line-height: 3em;

View File

@@ -37,11 +37,11 @@
.selector-wrapper {
.super-selector-search {
border-bottom: 1px solid $light-grey;
border-bottom: 1px solid $disabled-light;
padding: 10px 5px;
input {
border: 1px solid $light-grey;
border: 1px solid $disabled-light;
box-sizing: border-box;
border-radius: 4px;
width: 100%;

View File

@@ -3,16 +3,11 @@
require "open_food_network/feature_toggle"
class FeatureToggleConstraint
def initialize(feature_name, negate: false)
def initialize(feature_name)
@feature = feature_name
@negate = negate
end
def matches?(request)
enabled?(request) ^ @negate
end
def enabled?(request)
OpenFoodNetwork::FeatureToggle.enabled?(@feature, current_user(request))
end

View File

@@ -11,8 +11,9 @@ module Admin
@line_items = order_permissions.
editable_line_items.where(order_id: orders).
includes(:variant).
ransack(params[:q]).result.order(:id)
includes(variant: { option_values: :option_type }).
ransack(params[:q]).result.
reorder('spree_line_items.order_id ASC, spree_line_items.id ASC')
@pagy, @line_items = pagy(@line_items) if pagination_required?
@@ -34,8 +35,7 @@ module Admin
# and https://www.postgresql.org/docs/current/static/sql-select.html#SQL-FOR-UPDATE-SHARE
order.with_lock do
if order.contents.update_item(@line_item, line_item_params)
# No Content, does not trigger ng resource auto-update
render body: nil, status: :no_content
render body: nil, status: :no_content # No Content, does not trigger ng resource auto-update
else
render json: { errors: @line_item.errors }, status: :precondition_failed
end

View File

@@ -18,8 +18,6 @@ module Admin
end
end
ContentConfig.updated_at = Time.zone.now
flash[:success] =
t(:successfully_updated, resource: I18n.t('admin.contents.edit.your_content'))

View File

@@ -2,7 +2,6 @@
require 'open_food_network/address_finder'
# rubocop:disable Metrics/ClassLength
module Admin
class CustomersController < Admin::ResourceController
before_action :load_managed_shops, only: :index, if: :html_request?
@@ -36,10 +35,8 @@ module Admin
end
def create
@customer = Customer.find_or_initialize_by(customer_params.slice(:email, :enterprise_id))
@customer = Customer.new(customer_params)
if user_can_create_customer?
@customer.created_manually = true
if @customer.save
tag_rule_mapping = TagRule.mapping_for(Enterprise.where(id: @customer.enterprise))
render_as_json @customer, tag_rule_mapping: tag_rule_mapping
@@ -70,7 +67,7 @@ module Admin
def collection
if json_request? && params[:enterprise_id].present?
CustomersWithBalance.new(customers).query.
CustomersWithBalance.new(managed_enterprise_id).query.
includes(
:enterprise,
{ bill_address: [:state, :country] },
@@ -82,15 +79,6 @@ module Admin
end
end
def customers
return @customers if @customers.present?
@customers = Customer.visible.managed_by(spree_current_user)
return @customers if params[:enterprise_id].blank?
@customers = @customers.where(enterprise_id: params[:enterprise_id])
end
def managed_enterprise_id
@managed_enterprise_id ||= Enterprise.managed_by(spree_current_user).
select('enterprises.id').find_by(id: params[:enterprise_id])
@@ -132,4 +120,3 @@ module Admin
end
end
end
# rubocop:enable Metrics/ClassLength

View File

@@ -6,6 +6,7 @@ module Admin
class EnterpriseFeesController < Admin::ResourceController
before_action :load_enterprise_fee_set, only: :index
before_action :load_data
before_action :check_enterprise_fee_input, only: [:bulk_update]
def index
@include_calculators = params[:include_calculators].present?
@@ -23,6 +24,7 @@ module Admin
format.json {
render_as_json @collection, controller: self, include_calculators: @include_calculators
}
# format.json { @presented_collection = @collection.each_with_index.map { |ef, i| EnterpriseFeePresenter.new(self, ef, i) } }
end
end
@@ -34,8 +36,7 @@ module Admin
end
def bulk_update
# Forms has strong parameters, so we don't need to validate them in controller
@enterprise_fee_set = EnterpriseFeesBulkUpdate.new(params)
@enterprise_fee_set = Sets::EnterpriseFeeSet.new(enterprise_fee_bulk_params)
if @enterprise_fee_set.save
redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice)
@@ -61,7 +62,7 @@ module Admin
when :for_order_cycle
order_cycle = OrderCycle.find_by(id: params[:order_cycle_id]) if params[:order_cycle_id]
coordinator = Enterprise.find_by(id: params[:coordinator_id]) if params[:coordinator_id]
order_cycle ||= OrderCycle.new(coordinator: coordinator) if coordinator.present?
order_cycle = OrderCycle.new(coordinator: coordinator) if order_cycle.nil? && coordinator.present?
enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user,
order_cycle).visible_enterprises
EnterpriseFee.for_enterprises(enterprises).order('enterprise_id', 'fee_type', 'name')
@@ -88,5 +89,35 @@ module Admin
main_app.admin_enterprise_fees_path
end
def enterprise_fee_bulk_params
params.require(:sets_enterprise_fee_set).permit(
collection_attributes: [
:id, :enterprise_id, :fee_type, :name, :tax_category_id,
:inherits_tax_category, :calculator_type,
{ calculator_attributes: PermittedAttributes::Calculator.attributes }
]
)
end
def check_enterprise_fee_input
enterprise_fee_bulk_params['collection_attributes'].each do |_, fee_row|
enterprise_fees = fee_row['calculator_attributes']&.slice(
:preferred_flat_percent, :preferred_amount,
:preferred_first_item, :preferred_additional_item,
:preferred_minimal_amount, :preferred_normal_amount,
:preferred_discount_amount, :preferred_per_unit
)
next unless enterprise_fees
enterprise_fees.each do |_, enterprise_amount|
unless enterprise_amount.nil? || Float(enterprise_amount, exception: false)
flash[:error] = I18n.t(:calculator_preferred_value_error)
return redirect_to redirect_path
end
end
end
end
end
end

View File

@@ -16,8 +16,7 @@ module Admin
@enterprise_relationship = EnterpriseRelationship.new enterprise_relationship_params
if @enterprise_relationship.save
render plain: Api::Admin::EnterpriseRelationshipSerializer
.new(@enterprise_relationship).to_json
render plain: Api::Admin::EnterpriseRelationshipSerializer.new(@enterprise_relationship).to_json
else
render status: :bad_request,
json: { errors: @enterprise_relationship.errors.full_messages.join(', ') }

View File

@@ -8,12 +8,12 @@ module Admin
class EnterprisesController < Admin::ResourceController
include GeocodeEnterpriseAddress
include CablecarResponses
include Pagy::Backend
# These need to run before #load_resource so that @object is initialised with sanitised values
prepend_before_action :override_owner, only: :create
prepend_before_action :override_sells, only: :create
before_action :load_enterprise_set, only: :index
before_action :load_countries, except: [:index, :register, :check_permalink]
before_action :load_methods_and_fees, only: [:edit, :update]
before_action :load_groups, only: [:new, :edit, :update, :create]
@@ -29,11 +29,10 @@ module Admin
after_action :geocode_address_if_use_geocoder, only: [:create, :update]
helper 'spree/products'
include OrderCyclesHelper
def index
load_enterprise_set_on_index
respond_to do |format|
format.html
format.json {
@@ -46,11 +45,10 @@ module Admin
def edit
@object = Enterprise.where(permalink: params[:id]).
includes(users: [:ship_address, :bill_address]).first
@object.build_custom_tab if @object.custom_tab.nil?
if params[:stimulus]
@enterprise.is_primary_producer = params[:is_primary_producer]
@enterprise.sells = params[:enterprise_sells]
render cable_ready: cable_car.morph("#side_menu", partial("admin/shared/side_menu"))
render operations: cable_car.morph("#side_menu", partial("admin/shared/side_menu"))
.morph("#permalink", partial("admin/enterprises/form/permalink"))
end
end
@@ -64,8 +62,6 @@ module Admin
update_tag_rules(tag_rules_attributes) if tag_rules_attributes.present?
update_enterprise_notifications
delete_custom_tab if params[:custom_tab] == 'false'
if @object.update(enterprise_params)
flash[:success] = flash_message_for(@object, :successfully_updated)
respond_with(@object) do |format|
@@ -92,7 +88,7 @@ module Admin
return render :welcome, layout: "spree/layouts/bare_admin"
end
attributes = { sells: register_params[:sells], visible: "only_through_links" }
attributes = { sells: register_params[:sells], visible: true }
if @enterprise.update(attributes)
flash[:success] = I18n.t(:enterprise_register_success_notice, enterprise: @enterprise.name)
@@ -104,8 +100,7 @@ module Admin
end
def bulk_update
load_enterprise_set_with_params(bulk_params)
@enterprise_set = Sets::EnterpriseSet.new(collection, bulk_params)
if @enterprise_set.save
flash[:success] = I18n.t(:enterprise_bulk_update_success_notice)
@@ -121,12 +116,8 @@ module Admin
def for_order_cycle
respond_to do |format|
format.json do
render(
json: @collection,
each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer,
order_cycle: @order_cycle,
spree_current_user: spree_current_user
)
render json: @collection,
each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, order_cycle: @order_cycle, spree_current_user: spree_current_user
end
end
end
@@ -142,11 +133,6 @@ module Admin
protected
def delete_custom_tab
@object.custom_tab.destroy if @object.custom_tab.present?
enterprise_params.delete(:custom_tab_attributes)
end
def build_resource
enterprise = super
enterprise.address ||= Spree::Address.new
@@ -162,15 +148,8 @@ module Admin
private
def load_enterprise_set_on_index
return unless spree_current_user.admin?
load_enterprise_set_with_params
end
def load_enterprise_set_with_params(params = {})
@pagy, @paginated_collection = pagy(@collection)
@enterprise_set = Sets::EnterpriseSet.new(@paginated_collection, params)
def load_enterprise_set
@enterprise_set = Sets::EnterpriseSet.new(collection) if spree_current_user.admin?
end
def load_countries
@@ -182,7 +161,7 @@ module Admin
when :for_order_cycle
@order_cycle = OrderCycle.find_by(id: params[:order_cycle_id]) if params[:order_cycle_id]
coordinator = Enterprise.find_by(id: params[:coordinator_id]) if params[:coordinator_id]
@order_cycle ||= OrderCycle.new(coordinator: coordinator) if coordinator.present?
@order_cycle = OrderCycle.new(coordinator: coordinator) if @order_cycle.nil? && coordinator.present?
enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, @order_cycle)
.visible_enterprises
@@ -190,7 +169,7 @@ module Admin
if enterprises.present?
enterprises.includes(
supplied_products:
[:supplier, :variants, :image]
[:supplier, { master: [:images], variants: { option_values: :option_type } }]
)
end
when :index
@@ -199,8 +178,7 @@ module Admin
editable_enterprises.
order('is_primary_producer ASC, name')
elsif json_request?
OpenFoodNetwork::Permissions.new(spree_current_user)
.editable_enterprises.ransack(params[:q]).result
OpenFoodNetwork::Permissions.new(spree_current_user).editable_enterprises.ransack(params[:q]).result
else
Enterprise.where("1=0")
end
@@ -208,6 +186,7 @@ module Admin
OpenFoodNetwork::Permissions.new(spree_current_user).visible_enterprises
.includes(:shipping_methods, :payment_methods).ransack(params[:q]).result
else
# TODO was ordered with is_distributor DESC as well, not sure why or how we want to sort this now
OpenFoodNetwork::Permissions.new(spree_current_user).
editable_enterprises.
order('is_primary_producer ASC, name')
@@ -328,9 +307,7 @@ module Admin
:producer_properties_attributes).nil?
names = Spree::Property.pluck(:name)
enterprise_params[:producer_properties_attributes].each do |key, property|
unless names.include? property[:property_name]
enterprise_params[:producer_properties_attributes].delete key
end
enterprise_params[:producer_properties_attributes].delete key unless names.include? property[:property_name]
end
end
end

View File

@@ -18,6 +18,7 @@ module Admin
params.require(:preferences).permit(
:enable_invoices?,
:invoice_style2?,
:enable_receipt_printing?,
:enterprise_number_required_on_invoices?,
)
end

View File

@@ -0,0 +1,47 @@
# frozen_string_literal: true
module Admin
class ManagerInvitationsController < Spree::Admin::BaseController
authorize_resource class: false
def create
@email = params[:email]
@enterprise = Enterprise.find(params[:enterprise_id])
authorize! :edit, @enterprise
existing_user = Spree::User.find_by(email: @email)
if existing_user
render json: { errors: t('admin.enterprises.invite_manager.user_already_exists') },
status: :unprocessable_entity
return
end
new_user = create_new_manager
if new_user
render json: { user: new_user.id }, status: :ok
else
render json: { errors: t('admin.enterprises.invite_manager.error') },
status: :internal_server_error
end
end
private
def create_new_manager
password = Devise.friendly_token
new_user = Spree::User.create(email: @email, unconfirmed_email: @email, password: password)
new_user.reset_password_token = Devise.friendly_token
# Same time as used in Devise's lib/devise/models/recoverable.rb.
new_user.reset_password_sent_at = Time.now.utc
new_user.save!
@enterprise.users << new_user
EnterpriseMailer.manager_invitation(@enterprise, new_user).deliver_later
new_user
end
end
end

View File

@@ -100,13 +100,10 @@ module Admin
order_cycle.schedules.each do |schedule|
Subscription.where(schedule_id: schedule.id).each do |subscription|
shop = Enterprise.managed_by(spree_current_user).find_by(id: subscription.shop_id)
fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, order_cycle)
subscription.subscription_line_items.nil_price_estimate.each do |line_item|
variant = OrderManagement::Subscriptions::
VariantsList.eligible_variants(shop).find_by(id: line_item.variant_id)
# If the variant is not available in the shop, the price estimate will be nil
next if variant.nil?
fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, order_cycle)
price = variant.price + fee_calculator.indexed_fees_for(variant)
line_item.update_column(:price_estimate, price)
end

View File

@@ -37,14 +37,8 @@ module Admin
end
def reset_absent_products
@importer = ProductImport::ProductImporter.new(
File.new(file_path),
spree_current_user,
import_into: params[:import_into],
enterprises_to_reset: params[:enterprises_to_reset],
updated_ids: params[:updated_ids],
settings: params[:settings]
)
@importer = ProductImport::ProductImporter.new(File.new(params[:filepath]),
spree_current_user, import_into: params[:import_into], enterprises_to_reset: params[:enterprises_to_reset], updated_ids: params[:updated_ids], settings: params[:settings])
if params.key?(:enterprises_to_reset) && params.key?(:updated_ids)
@importer.reset_absent(params[:updated_ids])
@@ -62,13 +56,8 @@ module Admin
end
def process_data(method)
@importer = ProductImport::ProductImporter.new(
File.new(file_path),
spree_current_user,
start: params[:start],
end: params[:end],
settings: params[:settings]
)
@importer = ProductImport::ProductImporter.new(File.new(params[:filepath]),
spree_current_user, start: params[:start], end: params[:end], settings: params[:settings])
begin
@importer.public_send("#{method}_entries")
@@ -123,26 +112,5 @@ module Admin
def model_class
ProductImport::ProductImporter
end
def file_path
@file_path ||= validate_file_path(sanitize_file_path(params[:filepath]))
end
def sanitize_file_path(file_path)
FilePathSanitizer.new.sanitize(file_path, on_error: method(:raise_invalid_file_path))
end
def validate_file_path(file_path)
return file_path if file_path.to_s.match?(TEMP_FILE_PATH_REGEX)
raise_invalid_file_path
end
def raise_invalid_file_path
redirect_to '/admin/product_import',
notice: I18n.t(:product_import_no_data_in_spreadsheet_notice)
raise 'Invalid File Path'
end
TEMP_FILE_PATH_REGEX = %r{^/tmp/product_import[A-Za-z0-9-]*/import\.csv$}
end
end

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