Compare commits

..

1 Commits

Author SHA1 Message Date
Maikel Linke
fddb76f002 Merge branch 'active-storage' into HEAD
Creating a seperate release off the master branch.
2022-06-08 12:05:56 +10:00
790 changed files with 14239 additions and 46639 deletions

View File

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

View File

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

View File

@@ -1,11 +1,7 @@
# ENV vars for the development environment
# Override locally with `.env.development.local`
#
# You may also want to use this when testing other environments locally:
#
# cp .env.development .env.local
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
OFN_REDIS_URL="redis://localhost:6379/1"
OFN_REDIS_JOBS_URL="redis://localhost:6379/2"

View File

@@ -1,11 +1,6 @@
# ENV vars for the test environment
# Override locally with `.env.test.local`
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
STRIPE_SECRET_TEST_API_KEY="bogus_key"
STRIPE_CUSTOMER="bogus_customer"
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
SITE_URL="test.host"
OPENID_APP_ID="test-provider"
OPENID_APP_SECRET="12345"

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

13
.github/FUNDING.yml vendored
View File

@@ -1,13 +0,0 @@
# These are supported funding model platforms
github: openfoodfoundation
patreon: # Replace with a single Patreon username
open_collective: #
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -1,7 +1,7 @@
---
name: Release task
about: Track the process of a new release
title: Release v
title: 'Release v'
labels: ''
assignees: ''
@@ -10,6 +10,7 @@ assignees: ''
## Preparation on Thursday
- [ ] Merge pull requests in the [Ready To Go] column
- [ ] Merge [Transifex pull request]
- [ ] Include translations: `tx pull --force`
- [ ] [Draft new release]. Look at previous [releases] for inspiration.
- [ ] Notify [#instance-managers] of user-facing changes.
@@ -17,13 +18,26 @@ assignees: ''
## 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 -->
- [ ] Move this issue to Test Ready and notify testers.
- [ ] Test: :warning: link to the build of the release commit https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2/branches/master
## Finish on Tuesday
- [ ] Publish and notify [#global-community] (this is automatically posted with a plugin)
- [ ] Update translations unless content has been removed from config/locales/en.yml between this release draft and current master.
<details><summary>Command line instructions</summary>
<pre>
git checkout master # same version as the release draft
git fetch upstream
git diff upstream/master -- config/locales/en.yml
tx pull --force # if no changes or only additions in the locale
git checkout --detach # if we need to commit new translations
git commit -a -m "Update translations"
git tag vx.y.z # put the release number in here
git push upstream vx.y.z
</pre>
</details>
- [ ] 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>
@@ -43,6 +57,5 @@ The full process is described at https://github.com/openfoodfoundation/openfoodn
[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
[Find build]: https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2/branches/master
[#global-community]: https://app.slack.com/client/T02G54U79/C59ADD8F2

View File

@@ -1,6 +1,6 @@
#### What? Why?
- Closes # <!-- Insert issue number here. -->
Closes # <!-- Insert issue number here. -->
<!-- Explain why this change is needed and the solution you propose.
Provide context for others to understand it. -->

View File

@@ -10,30 +10,24 @@ on:
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
brakeman-scan:
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
name: Brakeman Scan
runs-on: ubuntu-latest
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
# Customize the ruby version depending on your needs
- name: Setup Ruby
uses: ruby/setup-ruby@v1
uses: actions/setup-ruby@v1
with:
ruby-version: '2.7'
- 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
@@ -45,6 +39,6 @@ jobs:
# Upload the SARIF file generated in the previous step
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: output.sarif.json

View File

@@ -3,22 +3,18 @@ name: Build
on:
workflow_dispatch:
push:
branches-ignore:
- 'dependabot/**'
pull_request:
env:
DISABLE_KNAPSACK_PRO: false
DISABLE_KNAPSACK: true
TIMEZONE: UTC
COVERAGE: true
RSPEC_RETRY_RETRY_COUNT: 3
RAILS_ENV: test
permissions:
contents: read
jobs:
knapsack_rspec_controllers:
runs-on: ubuntu-20.04
rspec:
runs-on: ubuntu-18.04
services:
postgres:
image: postgres:10
@@ -33,31 +29,30 @@ jobs:
POSTGRES_USER: ofn
POSTGRES_PASSWORD: f00d
strategy:
fail-fast: false
matrix:
# [n] - where the n is a number of parallel jobs you want to run your tests on.
# Use a higher number if you have slow tests to split them between more parallel jobs.
# Remember to update the value of the `ci_node_index` below to (0..n-1).
ci_node_total: [8]
# Indexes for parallel jobs (starting from zero).
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7]
specs:
- "spec/controllers"
- "spec/models"
- "spec/features/admin/[a-o0-9]*_spec.rb"
- "spec/lib"
- "spec/migrations"
- "spec/serializers"
- "spec/system/admin/[a-o0-9]*_spec.rb"
- "spec/system/admin/[p-z]*_spec.rb"
- "spec/system/consumer"
- "engines/*/spec"
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: actions/setup-node@v3
- uses: actions/setup-node@v2
with:
node-version: 16
node-version: '14.15.5'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
@@ -68,399 +63,19 @@ 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 }}
KNAPSACK_PRO_CI_NODE_INDEX: ${{ matrix.ci_node_index }}
KNAPSACK_PRO_LOG_LEVEL: info
# if you use Knapsack Pro Queue Mode you must set below env variable
# to be able to retry CI build and run previously recorded tests
# https://github.com/KnapsackPro/knapsack_pro-ruby#knapsack_pro_fixed_queue_split-remember-queue-split-on-retry-ci-node
# KNAPSACK_PRO_FIXED_QUEUE_SPLIT: false
# RSpec split test files by test examples feature - it's optional
# 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: |
bundle exec rake knapsack_pro:rspec
knapsack_rspec_models:
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
POSTGRES_DB: open_food_network_test
POSTGRES_USER: ofn
POSTGRES_PASSWORD: f00d
strategy:
fail-fast: false
matrix:
# [n] - where the n is a number of parallel jobs you want to run your tests on.
# Use a higher number if you have slow tests to split them between more parallel jobs.
# Remember to update the value of the `ci_node_index` below to (0..n-1).
ci_node_total: [7]
# Indexes for parallel jobs (starting from zero).
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
ci_node_index: [0, 1, 2, 3, 4, 5, 6]
steps:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Set up database
run: |
bundle exec rake db:create
bundle exec rake db:schema:load
- name: Run tests
env:
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: 09476e2ce491c12083df62768667c674
KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
KNAPSACK_PRO_CI_NODE_INDEX: ${{ matrix.ci_node_index }}
KNAPSACK_PRO_LOG_LEVEL: info
# if you use Knapsack Pro Queue Mode you must set below env variable
# to be able to retry CI build and run previously recorded tests
# https://github.com/KnapsackPro/knapsack_pro-ruby#knapsack_pro_fixed_queue_split-remember-queue-split-on-retry-ci-node
# KNAPSACK_PRO_FIXED_QUEUE_SPLIT: false
# RSpec split test files by test examples feature - it's optional
# 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/models/**/*_spec.rb}"
run: |
bundle exec rake knapsack_pro:rspec
knapsack_rspec_system_admin:
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
POSTGRES_DB: open_food_network_test
POSTGRES_USER: ofn
POSTGRES_PASSWORD: f00d
strategy:
fail-fast: false
matrix:
# [n] - where the n is a number of parallel jobs you want to run your tests on.
# Use a higher number if you have slow tests to split them between more parallel jobs.
# Remember to update the value of the `ci_node_index` below to (0..n-1).
ci_node_total: [10]
# Indexes for parallel jobs (starting from zero).
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
steps:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Set up database
run: |
bundle exec rake db:create
bundle exec rake db:schema:load
- name: Run tests
env:
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: ff2456e64c9f2aa5157eb0daf711d3c3
KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
KNAPSACK_PRO_CI_NODE_INDEX: ${{ matrix.ci_node_index }}
KNAPSACK_PRO_LOG_LEVEL: info
# if you use Knapsack Pro Queue Mode you must set below env variable
# to be able to retry CI build and run previously recorded tests
# https://github.com/KnapsackPro/knapsack_pro-ruby#knapsack_pro_fixed_queue_split-remember-queue-split-on-retry-ci-node
KNAPSACK_PRO_FIXED_QUEUE_SPLIT: true
# RSpec split test files by test examples feature - it's optional
# 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/system/admin/**/*_spec.rb}"
run: |
bundle exec rake knapsack_pro:queue:rspec
run: bundle exec rspec --profile -- ${{ matrix.specs }}
- name: Archive failed tests screenshots
if: failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: failed-tests-screenshots
path: tmp/capybara/screenshots/*.png
retention-days: 7
if-no-files-found: ignore
knapsack_rspec_system_consumer:
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
POSTGRES_DB: open_food_network_test
POSTGRES_USER: ofn
POSTGRES_PASSWORD: f00d
strategy:
fail-fast: false
matrix:
# [n] - where the n is a number of parallel jobs you want to run your tests on.
# Use a higher number if you have slow tests to split them between more parallel jobs.
# Remember to update the value of the `ci_node_index` below to (0..n-1).
ci_node_total: [10]
# Indexes for parallel jobs (starting from zero).
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
steps:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Set up database
run: |
bundle exec rake db:create
bundle exec rake db:schema:load
- name: Run tests
env:
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: e52bd4390c853e6c5bdfe4d0334586c1
KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
KNAPSACK_PRO_CI_NODE_INDEX: ${{ matrix.ci_node_index }}
KNAPSACK_PRO_LOG_LEVEL: info
# if you use Knapsack Pro Queue Mode you must set below env variable
# to be able to retry CI build and run previously recorded tests
# https://github.com/KnapsackPro/knapsack_pro-ruby#knapsack_pro_fixed_queue_split-remember-queue-split-on-retry-ci-node
KNAPSACK_PRO_FIXED_QUEUE_SPLIT: true
# RSpec split test files by test examples feature - it's optional
# 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/system/consumer/**/*_spec.rb}"
run: |
bundle exec rake knapsack_pro:queue:rspec
- name: Archive failed tests screenshots
if: failure()
uses: actions/upload-artifact@v3
with:
name: failed-tests-screenshots
path: tmp/capybara/screenshots/*.png
retention-days: 7
if-no-files-found: ignore
knapsack_rspec_engines:
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
POSTGRES_DB: open_food_network_test
POSTGRES_USER: ofn
POSTGRES_PASSWORD: f00d
strategy:
fail-fast: false
matrix:
# [n] - where the n is a number of parallel jobs you want to run your tests on.
# Use a higher number if you have slow tests to split them between more parallel jobs.
# Remember to update the value of the `ci_node_index` below to (0..n-1).
ci_node_total: [5]
# Indexes for parallel jobs (starting from zero).
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
ci_node_index: [0, 1, 2, 3, 4]
steps:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Set up database
run: |
bundle exec rake db:create
bundle exec rake db:schema:load
- name: Run tests
env:
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: d6ea7ceb766404ccd016c19aa2c81b1c
KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
KNAPSACK_PRO_CI_NODE_INDEX: ${{ matrix.ci_node_index }}
KNAPSACK_PRO_LOG_LEVEL: info
# if you use Knapsack Pro Queue Mode you must set below env variable
# to be able to retry CI build and run previously recorded tests
# https://github.com/KnapsackPro/knapsack_pro-ruby#knapsack_pro_fixed_queue_split-remember-queue-split-on-retry-ci-node
# KNAPSACK_PRO_FIXED_QUEUE_SPLIT: false
# RSpec split test files by test examples feature - it's optional
# 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/lib/**/*_spec.rb,spec/migrations/**/*_spec.rb,spec/serializers/**/*_spec.rb,engines/**/*_spec.rb}"
run: |
bundle exec rake knapsack_pro:rspec
- name: Archive failed tests screenshots
if: failure()
uses: actions/upload-artifact@v3
with:
name: failed-tests-screenshots
path: tmp/capybara/screenshots/*.png
retention-days: 7
if-no-files-found: ignore
knapsack_rspec_test_the_rest:
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
POSTGRES_DB: open_food_network_test
POSTGRES_USER: ofn
POSTGRES_PASSWORD: f00d
strategy:
fail-fast: false
matrix:
# [n] - where the n is a number of parallel jobs you want to run your tests on.
# Use a higher number if you have slow tests to split them between more parallel jobs.
# Remember to update the value of the `ci_node_index` below to (0..n-1).
ci_node_total: [5]
# Indexes for parallel jobs (starting from zero).
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
ci_node_index: [0, 1, 2, 3, 4]
steps:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Set up database
run: |
bundle exec rake db:create
bundle exec rake db:schema:load
- name: Run tests
env:
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: e3b8800198d2d89b70c7edbdd85f8fd8
KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
KNAPSACK_PRO_CI_NODE_INDEX: ${{ matrix.ci_node_index }}
KNAPSACK_PRO_LOG_LEVEL: info
# if you use Knapsack Pro Queue Mode you must set below env variable
# to be able to retry CI build and run previously recorded tests
# https://github.com/KnapsackPro/knapsack_pro-ruby#knapsack_pro_fixed_queue_split-remember-queue-split-on-retry-ci-node
# KNAPSACK_PRO_FIXED_QUEUE_SPLIT: false
# RSpec split test files by test examples feature - it's optional
# 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_EXCLUDE_PATTERN: "{engines/**/*_spec.rb,spec/models/**/*_spec.rb,spec/controllers/**/*_spec.rb,spec/serializers/**/*_spec.rb,spec/lib/**/*_spec.rb,spec/migrations/**/*_spec.rb,spec/system/**/*_spec.rb}"
run: |
bundle exec rake knapsack_pro:rspec
non_knapsack_jest_karma:
runs-on: ubuntu-20.04
test-the-rest:
runs-on: ubuntu-18.04
services:
postgres:
image: postgres:10
@@ -475,21 +90,16 @@ jobs:
POSTGRES_USER: ofn
POSTGRES_PASSWORD: f00d
steps:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: actions/setup-node@v3
- uses: actions/setup-node@v2
with:
node-version: 16
node-version: '14.15.5'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
@@ -498,8 +108,12 @@ jobs:
run: |
bundle exec rake db:create
bundle exec rake db:schema:load
- name: Run JS tests
run: bundle exec rake karma:run
- name: Run jest tests
run: yarn jest
- name: Run all other tests
run: bundle exec rake ofn:specs:run:excluding_folders["models,controllers,serializers,features,lib,migrations,system"]

View File

@@ -1,7 +1,5 @@
name: Linters
on: [push, pull_request]
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
rubocop:
name: runner / rubocop
@@ -18,16 +16,3 @@ jobs:
reporter: github-pr-check
level: error
fail_on_error: true
prettier:
name: runner / prettier
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: prettier
uses: EPMatt/reviewdog-action-prettier@v1
with:
github_token: ${{ secrets.github_token }}
reporter: github-pr-check
level: error
fail_on_error: true

View File

@@ -1,18 +1,13 @@
name: 'Mayhem for API'
on: workflow_dispatch
permissions:
contents: read # to fetch code (actions/checkout)
on: [push]
jobs:
test:
permissions:
contents: read # to fetch code (actions/checkout)
security-events: write # to upload SARIF results (github/codeql-action/upload-sarif)
if: ${{ github.repository_owner == 'openfoodfoundation' }}
runs-on: ubuntu-latest
strategy:
fail-fast: true
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- run: docker/build
- run: docker-compose up --detach
- run: until curl -f -s http://localhost:3000; do echo "waiting for api server"; sleep 1; done
@@ -39,13 +34,13 @@ jobs:
# Archive HTML report
- name: Archive Mayhem for API report
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: mapi-report
path: mapi.html
# Upload SARIF file (only available on public repos or github enterprise)
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: mapi.sarif

4
.gitignore vendored
View File

@@ -50,12 +50,10 @@ vendor/bundle/
coverage
/reports/
!/reports/README.md
/spec/components/stories/**/*.stories.json
/public/packs
/public/packs-test
/yarn-error.log
yarn-debug.log*
.yarn-integrity
/config/credentials.yml.enc
/config/master.key

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn pretty-quick --check --staged

View File

@@ -1,22 +0,0 @@
# 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
babel.config.js
postcss.config.js
/app/assets/
/config/
/coverage/
/engines/
/public/
/spec/
/tmp/
/vendor/

View File

@@ -1 +0,0 @@
{}

View File

@@ -23,7 +23,7 @@ Metrics:
Enabled: true
Metrics/BlockLength:
AllowedMethods: [
IgnoredMethods: [
"class_eval",
"collection",
"context",
@@ -41,14 +41,8 @@ Metrics/BlockLength:
"resources",
"scenario",
"shared_examples",
"xdescribe",
]
Rails/ApplicationRecord:
Exclude:
# Migrations should not contain application code:
- "db/migrate/*.rb"
Rails/SkipsModelValidations:
AllowedMethods:
- "touch"

View File

@@ -1,13 +1,13 @@
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400`
# on 2022-08-29 05:26:26 UTC using RuboCop version 1.35.1.
# on 2022-03-29 16:07:39 UTC using RuboCop version 1.22.2.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
# Include: **/*.gemfile, **/Gemfile, **/gems.rb
Bundler/OrderedGems:
@@ -25,13 +25,20 @@ Gemspec/RequiredRubyVersion:
- 'engines/web/web.gemspec'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Cop supports --auto-correct.
Layout/ClosingParenthesisIndentation:
Exclude:
- 'lib/reporting/queries/joins.rb'
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Cop supports --auto-correct.
# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, AllowAdjacentOneLineDefs, NumberOfEmptyLines.
Layout/EmptyLineBetweenDefs:
Exclude:
- 'spec/lib/reports/report_loader_spec.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: empty_lines, no_empty_lines
Layout/EmptyLinesAroundBlockBody:
@@ -40,15 +47,15 @@ Layout/EmptyLinesAroundBlockBody:
- 'spec/system/admin/order_cycles/list_spec.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Cop supports --auto-correct.
# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment.
Layout/LeadingCommentSpace:
Exclude:
- 'spec/system/admin/enterprises_spec.rb'
# Offense count: 862
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns.
# Offense count: 856
# Cop supports --auto-correct.
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Layout/LineLength:
Exclude:
@@ -61,6 +68,7 @@ Layout/LineLength:
- 'app/controllers/admin/subscriptions_controller.rb'
- 'app/controllers/api/v0/order_cycles_controller.rb'
- 'app/controllers/payment_gateways/paypal_controller.rb'
- 'app/controllers/spree/admin/reports_controller.rb'
- 'app/controllers/spree/users_controller.rb'
- 'app/controllers/user_confirmations_controller.rb'
- 'app/helpers/angular_form_builder.rb'
@@ -77,6 +85,7 @@ Layout/LineLength:
- 'app/models/concerns/variant_stock.rb'
- 'app/models/customer.rb'
- 'app/models/enterprise.rb'
- 'app/models/enterprise_group.rb'
- 'app/models/product_import/entry_processor.rb'
- 'app/models/product_import/spreadsheet_entry.rb'
- 'app/models/product_import/unit_converter.rb'
@@ -102,15 +111,23 @@ Layout/LineLength:
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
- 'engines/order_management/spec/services/order_management/order/updater_spec.rb'
- 'engines/web/app/helpers/web/cookies_policy_helper.rb'
- 'engines/web/spec/features/consumer/cookies_spec.rb'
- 'lib/discourse/single_sign_on.rb'
- 'lib/open_food_network/enterprise_fee_applicator.rb'
- 'lib/open_food_network/enterprise_fee_calculator.rb'
- 'lib/open_food_network/enterprise_issue_validator.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/open_food_network/scope_variants_for_search.rb'
- 'lib/reporting/line_items.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/enterprise_fee_summary/report_data/enterprise_fee_type_total.rb'
- 'lib/reporting/reports/order_cycle_management/order_cycle_management_report.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/reporting/reports/orders_and_fulfillment/customer_totals_report.rb'
- 'lib/reporting/reports/orders_and_fulfillment/distributor_totals_by_supplier_report.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
- 'lib/reporting/reports/products_and_inventory/lettuce_share_report.rb'
- 'lib/reporting/reports/sales_tax/sales_tax_report.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
- 'lib/spree/localized_number.rb'
- 'lib/tasks/data.rake'
@@ -128,10 +145,12 @@ Layout/LineLength:
- 'spec/controllers/admin/subscriptions_controller_spec.rb'
- 'spec/controllers/admin/variant_overrides_controller_spec.rb'
- 'spec/controllers/api/v0/base_controller_spec.rb'
- 'spec/controllers/api/v0/enterprises_controller_spec.rb'
- 'spec/controllers/api/v0/exchange_products_controller_spec.rb'
- 'spec/controllers/api/v0/logos_controller_spec.rb'
- 'spec/controllers/api/v0/order_cycles_controller_spec.rb'
- 'spec/controllers/api/v0/orders_controller_spec.rb'
- 'spec/controllers/api/v0/product_images_controller_spec.rb'
- 'spec/controllers/api/v0/products_controller_spec.rb'
- 'spec/controllers/api/v0/promo_images_controller_spec.rb'
- 'spec/controllers/api/v0/terms_and_conditions_controller_spec.rb'
@@ -145,6 +164,7 @@ Layout/LineLength:
- 'spec/controllers/spree/admin/orders/invoices_spec.rb'
- 'spec/controllers/spree/admin/orders_controller_spec.rb'
- 'spec/controllers/spree/admin/payment_methods_controller_spec.rb'
- 'spec/controllers/spree/admin/reports_controller_spec.rb'
- 'spec/controllers/spree/admin/variants_controller_spec.rb'
- 'spec/controllers/spree/credit_cards_controller_spec.rb'
- 'spec/controllers/spree/orders_controller_spec.rb'
@@ -167,9 +187,13 @@ Layout/LineLength:
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
- 'spec/lib/reports/customers_report_spec.rb'
- 'spec/lib/reports/order_cycle_management_report_spec.rb'
- 'spec/lib/reports/order_grouper_spec.rb'
- 'spec/lib/reports/orders_and_fulfillment/orders_and_fulfillment_report_spec.rb'
- 'spec/lib/reports/packing/packing_report_spec.rb'
- 'spec/lib/reports/products_and_inventory_report_spec.rb'
- 'spec/lib/reports/users_and_enterprises_report_spec.rb'
- 'spec/lib/reports/xero_invoices_report_spec.rb'
- 'spec/lib/stripe/authorize_response_patcher_spec.rb'
- 'spec/mailers/order_mailer_spec.rb'
- 'spec/mailers/producer_mailer_spec.rb'
- 'spec/mailers/subscription_mailer_spec.rb'
@@ -177,6 +201,7 @@ Layout/LineLength:
- 'spec/models/concerns/calculated_adjustments_spec.rb'
- 'spec/models/concerns/order_shipment_spec.rb'
- 'spec/models/concerns/product_stock_spec.rb'
- 'spec/models/enterprise_group_spec.rb'
- 'spec/models/enterprise_relationship_spec.rb'
- 'spec/models/enterprise_spec.rb'
- 'spec/models/exchange_spec.rb'
@@ -202,6 +227,7 @@ Layout/LineLength:
- 'spec/models/tag_rule/filter_payment_methods_spec.rb'
- 'spec/models/tag_rule/filter_products_spec.rb'
- 'spec/models/tag_rule/filter_shipping_methods_spec.rb'
- 'spec/models/terms_of_service_file_spec.rb'
- 'spec/models/variant_override_spec.rb'
- 'spec/requests/api/orders_spec.rb'
- 'spec/requests/checkout/failed_checkout_spec.rb'
@@ -261,7 +287,6 @@ Layout/LineLength:
- 'spec/system/admin/variant_overrides_spec.rb'
- 'spec/system/consumer/authentication_spec.rb'
- 'spec/system/consumer/caching/shops_caching_spec.rb'
- 'spec/system/consumer/cookies_spec.rb'
- 'spec/system/consumer/shopping/cart_spec.rb'
- 'spec/system/consumer/shopping/checkout_auth_spec.rb'
- 'spec/system/consumer/shopping/checkout_spec.rb'
@@ -275,20 +300,35 @@ Layout/LineLength:
- 'spec/views/spree/admin/payment_methods/index.html.haml_spec.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Cop supports --auto-correct.
Layout/MultilineBlockLayout:
Exclude:
- 'spec/lib/reports/report_renderer_spec.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: symmetrical, new_line, same_line
Layout/MultilineMethodCallBraceLayout:
Exclude:
- 'lib/reporting/queries/joins.rb'
# Offense count: 22
# This cop supports safe autocorrection (--autocorrect).
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: aligned, indented, indented_relative_to_receiver
Layout/MultilineMethodCallIndentation:
Exclude:
- 'lib/reporting/reports/customers/customers_report.rb'
# Offense count: 20
# Cop supports --auto-correct.
# Configuration parameters: AllowInHeredoc.
Layout/TrailingWhitespace:
Exclude:
- 'app/controllers/spree/admin/shipping_methods_controller.rb'
- 'spec/controllers/spree/admin/shipping_methods_controller_spec.rb'
- 'spec/system/admin/enterprises_spec.rb'
- 'spec/system/admin/order_spec.rb'
- 'spec/system/admin/shipping_methods_spec.rb'
- 'spec/system/flatpickr_spec.rb'
@@ -317,7 +357,6 @@ Lint/DuplicateMethods:
- 'lib/discourse/single_sign_on.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
Lint/DuplicateRequire:
Exclude:
- 'spec/lib/open_food_network/scope_variants_to_search_spec.rb'
@@ -339,8 +378,9 @@ Lint/IneffectiveAccessModifier:
Exclude:
- 'app/models/spree/user.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
# Configuration parameters: AllowedMethods.
# AllowedMethods: instance_of?, kind_of?, is_a?, eql?, respond_to?, equal?
Lint/RedundantSafeNavigation:
@@ -348,7 +388,6 @@ Lint/RedundantSafeNavigation:
- 'app/models/spree/payment.rb'
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowedMethods.
# AllowedMethods: present?, blank?, presence, try, try!, in?
Lint/SafeNavigationChain:
@@ -357,21 +396,22 @@ Lint/SafeNavigationChain:
- 'app/models/spree/stock/availability_validator.rb'
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Cop supports --auto-correct.
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods.
Lint/UnusedMethodArgument:
Exclude:
- 'lib/reporting/queries/query_interface.rb'
# Offense count: 5
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
# Configuration parameters: AllowComments.
Lint/UselessMethodDefinition:
Exclude:
- 'app/controllers/spree/user_registrations_controller.rb'
- 'app/models/spree/gateway.rb'
# Offense count: 28
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes, Max.
# Offense count: 38
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes, Max.
Metrics/AbcSize:
Exclude:
- 'app/controllers/admin/enterprises_controller.rb'
@@ -393,14 +433,20 @@ Metrics/AbcSize:
- 'app/models/spree/return_authorization.rb'
- 'lib/discourse/single_sign_on.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/customers/customers_report.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/reporting/reports/orders_and_distributors/orders_and_distributors_report.rb'
- 'lib/reporting/reports/packing/customer.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
- 'lib/reporting/reports/sales_tax/sales_tax_report.rb'
- 'lib/spree/core/controller_helpers/order.rb'
- 'lib/tasks/enterprises.rake'
- 'spec/services/order_checkout_restart_spec.rb'
# Offense count: 42
# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
# AllowedMethods: refine
# Offense count: 43
# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods.
# IgnoredMethods: refine
Metrics/BlockLength:
Exclude:
- 'app/models/spree/order/checkout.rb'
@@ -428,6 +474,7 @@ Metrics/BlockLength:
- 'spec/support/matchers/select2_matchers.rb'
- 'spec/support/matchers/table_matchers.rb'
- 'spec/swagger_helper.rb'
- 'spec/system/admin/order_cycles/complex_updating_specific_time_spec.rb'
- 'spec/system/consumer/shopping/checkout_spec.rb'
# Offense count: 1
@@ -436,11 +483,10 @@ Metrics/BlockNesting:
Exclude:
- 'app/models/spree/payment/processing.rb'
# Offense count: 45
# Offense count: 50
# Configuration parameters: CountComments, Max, CountAsOne.
Metrics/ClassLength:
Exclude:
- 'app/components/products_table_component.rb'
- 'app/controllers/admin/customers_controller.rb'
- 'app/controllers/admin/enterprises_controller.rb'
- 'app/controllers/admin/order_cycles_controller.rb'
@@ -456,6 +502,7 @@ Metrics/ClassLength:
- 'app/controllers/spree/admin/payment_methods_controller.rb'
- 'app/controllers/spree/admin/payments_controller.rb'
- 'app/controllers/spree/admin/products_controller.rb'
- 'app/controllers/spree/admin/reports_controller.rb'
- 'app/controllers/spree/admin/users_controller.rb'
- 'app/controllers/spree/orders_controller.rb'
- 'app/models/enterprise.rb'
@@ -479,17 +526,19 @@ Metrics/ClassLength:
- 'app/serializers/api/enterprise_shopfront_serializer.rb'
- 'app/services/cart_service.rb'
- 'app/services/order_syncer.rb'
- 'app/services/order_cycle_form.rb'
- 'engines/order_management/app/services/order_management/order/updater.rb'
- 'lib/open_food_network/enterprise_fee_calculator.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/open_food_network/permissions.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
- 'lib/reporting/reports/order_cycle_management/order_cycle_management_report.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
# Offense count: 35
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, Max.
# Offense count: 39
# Configuration parameters: IgnoredMethods, Max.
Metrics/CyclomaticComplexity:
Exclude:
- 'app/controllers/admin/enterprises_controller.rb'
@@ -516,14 +565,18 @@ Metrics/CyclomaticComplexity:
- 'app/models/spree/zone.rb'
- 'lib/discourse/single_sign_on.rb'
- 'lib/open_food_network/enterprise_issue_validator.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/customers/customers_report.rb'
- 'lib/reporting/reports/orders_and_fulfillment/customer_totals_report.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
- 'lib/spree/core/controller_helpers/order.rb'
- 'lib/spree/core/controller_helpers/respond_with.rb'
- 'lib/spree/localized_number.rb'
- 'spec/models/product_importer_spec.rb'
# Offense count: 26
# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
# Offense count: 32
# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods.
Metrics/MethodLength:
Exclude:
- 'app/controllers/admin/enterprises_controller.rb'
@@ -540,12 +593,15 @@ Metrics/MethodLength:
- 'app/models/spree/preferences/preferable_class_methods.rb'
- 'lib/discourse/single_sign_on.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
- 'lib/reporting/reports/order_cycle_management/order_cycle_management_report.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
- 'lib/tasks/sample_data/product_factory.rb'
# Offense count: 51
# Offense count: 54
# Configuration parameters: CountComments, Max, CountAsOne.
Metrics/ModuleLength:
Exclude:
@@ -585,6 +641,9 @@ Metrics/ModuleLength:
- 'spec/lib/reports/customers_report_spec.rb'
- 'spec/lib/reports/enterprise_fee_summary/authorizer_spec.rb'
- 'spec/lib/reports/order_cycle_management_report_spec.rb'
- 'spec/lib/reports/order_grouper_spec.rb'
- 'spec/lib/reports/orders_and_fulfillment/customer_totals_report_spec.rb'
- 'spec/lib/reports/orders_and_fulfillment/orders_and_fulfillment_report_spec.rb'
- 'spec/lib/reports/products_and_inventory_report_spec.rb'
- 'spec/lib/reports/users_and_enterprises_report_spec.rb'
- 'spec/models/spree/adjustment_spec.rb'
@@ -611,8 +670,8 @@ Metrics/ParameterLists:
- 'spec/support/controller_requests_helper.rb'
- 'spec/system/admin/reports_spec.rb'
# Offense count: 5
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, Max.
# Offense count: 7
# Configuration parameters: IgnoredMethods, Max.
Metrics/PerceivedComplexity:
Exclude:
- 'app/controllers/spree/admin/taxons_controller.rb'
@@ -620,8 +679,10 @@ Metrics/PerceivedComplexity:
- 'app/models/enterprise_relationship.rb'
- 'app/models/spree/ability.rb'
- 'app/models/spree/order/checkout.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
# Offense count: 8
# Offense count: 9
Naming/AccessorMethodName:
Exclude:
- 'app/controllers/spree/admin/taxonomies_controller.rb'
@@ -655,8 +716,8 @@ Naming/MethodParameterName:
Exclude:
- 'app/services/process_payment_intent.rb'
# Offense count: 28
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
# Offense count: 30
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers.
# SupportedStyles: snake_case, normalcase, non_integer
# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339
Naming/VariableNumber:
@@ -664,6 +725,7 @@ Naming/VariableNumber:
- 'app/controllers/spree/orders_controller.rb'
- 'app/models/content_configuration.rb'
- 'app/models/preference_sections/main_links_section.rb'
- 'lib/reporting/reports/orders_and_fulfillment/customer_totals_report.rb'
- 'lib/spree/core/controller_helpers/common.rb'
- 'spec/controllers/spree/admin/search_controller_spec.rb'
- 'spec/factories/stock_location_factory.rb'
@@ -679,19 +741,36 @@ Rails/ActiveRecordOverride:
- 'app/models/spree/product.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
Rails/ApplicationController:
Exclude:
- 'engines/dfc_provider/app/controllers/dfc_provider/base_controller.rb'
- 'engines/dfc_provider/app/controllers/dfc_provider/api/base_controller.rb'
# Offense count: 6
# Cop supports --auto-correct.
Rails/ApplicationJob:
Exclude:
- 'app/jobs/bulk_invoice_job.rb'
- 'app/jobs/heartbeat_job.rb'
- 'app/jobs/order_cycle_closing_job.rb'
- 'app/jobs/order_cycle_notification_job.rb'
- 'app/jobs/subscription_confirm_job.rb'
- 'app/jobs/subscription_placement_job.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
Rails/ApplicationMailer:
Exclude:
- 'app/mailers/spree/base_mailer.rb'
# Offense count: 1
# Cop supports --auto-correct.
Rails/ApplicationRecord:
Exclude:
- 'lib/tasks/data/remove_transient_data.rb'
# Offense count: 5
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent.
Rails/Blank:
Exclude:
@@ -717,7 +796,7 @@ Rails/FilePath:
- 'spec/models/content_configuration_spec.rb'
- 'spec/support/downloads_helper.rb'
# Offense count: 12
# Offense count: 11
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/HasAndBelongsToMany:
@@ -725,7 +804,6 @@ Rails/HasAndBelongsToMany:
- 'app/models/concerns/payment_method_distributors.rb'
- 'app/models/enterprise.rb'
- 'app/models/enterprise_group.rb'
- 'app/models/order_cycle.rb'
- 'app/models/spree/line_item.rb'
- 'app/models/spree/option_value.rb'
- 'app/models/spree/role.rb'
@@ -734,7 +812,7 @@ Rails/HasAndBelongsToMany:
- 'app/models/spree/variant.rb'
- 'app/models/spree/zone.rb'
# Offense count: 45
# Offense count: 47
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/HasManyOrHasOneDependent:
@@ -760,8 +838,9 @@ Rails/HasManyOrHasOneDependent:
- 'app/models/spree/taxonomy.rb'
- 'app/models/spree/user.rb'
- 'app/models/spree/variant.rb'
- 'app/models/subscription.rb'
# Offense count: 62
# Offense count: 59
# Configuration parameters: Include.
# Include: app/helpers/**/*.rb
Rails/HelperInstanceVariable:
@@ -802,7 +881,7 @@ Rails/InverseOf:
# Offense count: 38
# Configuration parameters: Include.
# Include: app/controllers/**/*.rb, app/mailers/**/*.rb
# Include: app/controllers/**/*.rb
Rails/LexicallyScopedActionFilter:
Exclude:
- 'app/controllers/admin/enterprise_groups_controller.rb'
@@ -825,9 +904,10 @@ Rails/LexicallyScopedActionFilter:
- 'app/controllers/spree/admin/zones_controller.rb'
- 'app/controllers/spree/users_controller.rb'
# Offense count: 18
# Offense count: 19
Rails/OutputSafety:
Exclude:
- 'app/controllers/spree/admin/reports_controller.rb'
- 'app/helpers/angular_form_helper.rb'
- 'app/helpers/application_helper.rb'
- 'app/helpers/reports_helper.rb'
@@ -842,7 +922,7 @@ Rails/OutputSafety:
- 'spec/system/admin/order_print_ticket_spec.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
Rails/RelativeDateConstant:
Exclude:
- 'lib/tasks/data/remove_transient_data.rb'
@@ -856,7 +936,7 @@ Rails/SkipsModelValidations:
- 'spec/models/spree/line_item_spec.rb'
# Offense count: 5
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: strict, flexible
Rails/TimeZone:
@@ -891,20 +971,20 @@ Security/Open:
- 'app/services/image_importer.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Cop supports --auto-correct.
Style/BlockComments:
Exclude:
- 'spec/system/admin/tag_rules_spec.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Cop supports --auto-correct.
# Configuration parameters: AllowOnConstant.
Style/CaseEquality:
Exclude:
- 'spec/models/spree/payment_spec.rb'
# Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
Style/CaseLikeIf:
Exclude:
- 'app/controllers/admin/order_cycles_controller.rb'
@@ -912,7 +992,7 @@ Style/CaseLikeIf:
- 'app/models/spree/payment/processing.rb'
# Offense count: 25
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: nested, compact
Style/ClassAndModuleChildren:
@@ -947,14 +1027,13 @@ Style/ClassVars:
- 'lib/spree/core/delegate_belongs_to.rb'
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns, IgnoredMethods.
# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, IgnoredMethods.
# SupportedStyles: annotated, template, unannotated
Style/FormatStringToken:
EnforcedStyle: unannotated
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment:
@@ -962,7 +1041,7 @@ Style/FrozenStringLiteralComment:
- '.simplecov'
# Offense count: 6
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
Style/GlobalStdStream:
Exclude:
- 'lib/tasks/data.rake'
@@ -971,8 +1050,8 @@ Style/GlobalStdStream:
- 'lib/tasks/subscriptions/debug.rake'
- 'lib/tasks/subscriptions/test.rake'
# Offense count: 40
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
# Offense count: 39
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Exclude:
- 'app/controllers/admin/enterprises_controller.rb'
@@ -1006,8 +1085,8 @@ Style/HashLikeCase:
- 'app/models/enterprise.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods.
# Cop supports --auto-correct.
# Configuration parameters: IgnoredMethods.
Style/MethodCallWithoutArgsParentheses:
Exclude:
- 'spec/system/flatpickr_spec.rb'
@@ -1019,8 +1098,22 @@ Style/MissingRespondToMissing:
- 'app/models/spree/gateway.rb'
- 'app/models/spree/preferences/configuration.rb'
# Offense count: 1
Style/MixinUsage:
Exclude:
- 'lib/reporting/reports/orders_and_fulfillment/orders_and_fulfillment_report.rb'
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: literals, strict
Style/MutableConstant:
Exclude:
- 'lib/reporting/report_template.rb'
- 'lib/reporting/reports/packing/base.rb'
# Offense count: 22
# This cop supports safe autocorrection (--autocorrect).
# Cop supports --auto-correct.
Style/NestedModifier:
Exclude:
- 'spec/controllers/admin/subscriptions_controller_spec.rb'
@@ -1034,7 +1127,7 @@ Style/NestedModifier:
- 'spec/system/admin/payments_stripe_spec.rb'
- 'spec/system/admin/reports_spec.rb'
# Offense count: 17
# Offense count: 26
# Configuration parameters: AllowedMethods.
# AllowedMethods: respond_to_missing?
Style/OptionalBooleanParameter:
@@ -1046,6 +1139,7 @@ Style/OptionalBooleanParameter:
- 'app/models/enterprise_relationship.rb'
- 'app/models/product_import/entry_processor.rb'
- 'app/models/spree/order_contents.rb'
- 'app/models/spree/preferences/file_configuration.rb'
- 'app/models/spree/shipment.rb'
- 'engines/order_management/app/services/order_management/stock/estimator.rb'
- 'lib/spree/core/controller_helpers/order.rb'
@@ -1053,7 +1147,7 @@ Style/OptionalBooleanParameter:
- 'spec/support/request/web_helper.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: short, verbose
Style/PreferredHashMethods:
@@ -1061,13 +1155,13 @@ Style/PreferredHashMethods:
- 'app/controllers/api/v0/shipments_controller.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Cop supports --auto-correct.
# Configuration parameters: AllowMultipleReturnValues.
Style/RedundantReturn:
Exclude:
- 'app/controllers/spree/admin/shipping_methods_controller.rb'
# Offense count: 209
# Offense count: 205
Style/Send:
Exclude:
- 'app/controllers/split_checkout_controller.rb'
@@ -1101,16 +1195,17 @@ Style/Send:
- 'spec/services/cart_service_spec.rb'
- 'spec/services/products_renderer_spec.rb'
- 'spec/services/variant_units/option_value_namer_spec.rb'
- 'spec/spec_helper.rb'
- 'spec/support/localized_number_helper.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
Style/SingleArgumentDig:
Exclude:
- 'app/services/checkout/form_data_adapter.rb'
# Offense count: 4
# This cop supports unsafe autocorrection (--autocorrect-all).
# Cop supports --auto-correct.
Style/SlicingWithRange:
Exclude:
- 'app/helpers/spree/admin/navigation_helper.rb'
@@ -1118,8 +1213,8 @@ Style/SlicingWithRange:
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
- 'lib/discourse/single_sign_on.rb'
# Offense count: 29
# This cop supports unsafe autocorrection (--autocorrect-all).
# Offense count: 28
# Cop supports --auto-correct.
# Configuration parameters: Mode.
Style/StringConcatenation:
Exclude:

7
.storybook/main.js Normal file
View File

@@ -0,0 +1,7 @@
module.exports = {
stories: ['../spec/components/stories/**/*.stories.json'],
addons: [
'@storybook/addon-docs',
'@storybook/addon-controls',
],
};

View File

@@ -0,0 +1,2 @@
<link href='https://fonts.googleapis.com/css?family=Roboto:400,300italic,400italic,300,700,700italic|Oswald:300,400,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" media="screen" href="http://localhost:3000/assets/darkswarm/all.css" />

5
.storybook/preview.js Normal file
View File

@@ -0,0 +1,5 @@
export const parameters = {
server: {
url: `http://localhost:3000/rails/stories`,
},
};

View File

@@ -1,7 +1,7 @@
[main]
host = https://www.transifex.com
[o:open-food-foundation:p:open-food-network:r:enyml]
[open-food-network.enyml]
file_filter = config/locales/<lang>.yml
source_lang = en
type = YML

View File

@@ -35,10 +35,7 @@ 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} && \

View File

@@ -28,7 +28,7 @@ For those new to Rails, the following tutorial will help get you up to speed wit
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:
@@ -36,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):
@@ -68,8 +68,6 @@ To login as the default user, use:
email: ofn@example.com
password: ofn123
Seee [Locale and sample data] about loading data.
### Testing
@@ -83,7 +81,7 @@ Then the main application tests can be run with:
The tests of all custom engines can be run with:
bundle exec rspec ./engines
bundle exec rake ofn:specs:engines:rspec
Note: If your OS is not explicitly supported in the setup guides then not all tests may pass. However, you may still be able to develop.
@@ -131,4 +129,3 @@ If these commands succeed, you should be able to [continue the setup process](#g
[rubocop]: https://rubocop.readthedocs.io/en/latest/
[karma]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Karma
[slack-dev]: https://openfoodnetwork.slack.com/messages/C2GQ45KNU
[Locale and sample data]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Locale-and-sample-data

38
Gemfile
View File

@@ -29,7 +29,6 @@ gem 'rails_safe_tasks', '~> 1.0'
gem "activerecord-import"
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-6"
gem "fog-aws", "~> 2.0" # db2fog does not support v3
gem "mime-types" # required by fog
gem "valid_email2"
@@ -69,11 +68,8 @@ gem 'pagy', '~> 5.1'
gem 'rswag-api'
gem 'rswag-ui'
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 'aws-sdk', '1.67.0'
gem 'bugsnag'
gem 'haml'
gem 'redcarpet'
@@ -92,6 +88,7 @@ gem 'gmaps4rails'
gem 'mimemagic', '> 0.3.5'
gem 'paper_trail', '~> 12.1.0'
gem 'rack-rewrite'
gem 'rack-ssl', require: 'rack/ssl'
gem 'roadie-rails'
gem 'hiredis'
@@ -100,15 +97,14 @@ gem 'redis', '>= 4.0', require: ['redis', 'redis/connection/hiredis']
gem 'sidekiq'
gem 'sidekiq-scheduler'
gem "cable_ready", "5.0.0.pre9"
gem "stimulus_reflex", "3.5.0.pre9"
gem "cable_ready", "5.0.0.pre3"
gem 'combine_pdf'
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
gem 'immigrant'
gem 'roo'
gem 'roo', github: "roo-rb/roo" # master is currently needed for Ruby 3.x (awaiting new release)
gem 'spreadsheet_architect'
gem 'whenever', require: false
@@ -117,7 +113,9 @@ gem 'test-unit', '~> 3.5'
gem 'coffee-rails', '~> 5.0.0'
gem 'mini_racer'
gem 'mini_racer', '0.4.0'
gem 'uglifier', '>= 1.0.3'
gem 'angular_rails_csrf'
@@ -133,10 +131,7 @@ gem 'flipper'
gem 'flipper-active_record'
gem 'flipper-ui'
gem "view_component"
gem 'view_component_reflex', '3.1.14.pre9'
gem 'mini_portile2', '~> 2.8'
gem "view_component", require: "view_component/engine"
group :production, :staging do
gem 'ddtrace'
@@ -145,6 +140,8 @@ group :production, :staging do
end
group :test, :development do
# Pretty printed test output
gem 'awesome_print'
gem 'bullet'
gem 'capybara'
gem 'cuprite'
@@ -152,38 +149,41 @@ group :test, :development do
gem "factory_bot_rails", '6.2.0', require: false
gem 'fuubar', '~> 2.5.1'
gem 'json_spec', '~> 1.1.4'
gem 'knapsack_pro'
gem 'knapsack'
gem 'letter_opener', '>= 1.4.1'
gem 'rspec-rails', ">= 3.5.2"
gem 'rspec-retry', require: false
gem 'rspec-retry'
gem 'rswag-specs'
gem 'selenium-webdriver'
gem 'shoulda-matchers'
gem 'timecop'
gem 'debug', '>= 1.0.0'
gem 'webdrivers'
end
group :test do
gem 'byebug'
gem 'pdf-reader'
gem 'rails-controller-testing'
gem 'simplecov', require: false
gem 'vcr', require: false
gem 'webmock', require: false
gem 'test-prof'
gem 'webmock'
# See spec/spec_helper.rb for instructions
# gem 'perftools.rb'
end
group :development do
gem 'debugger-linecache'
gem 'rails-erd'
gem 'foreman'
gem 'listen'
gem 'pry', '~> 0.13.0'
gem 'pry-byebug', '~> 3.9.0'
gem 'rubocop'
gem 'rubocop-rails'
gem 'spring'
gem 'spring-commands-rspec'
gem 'web-console'
gem "view_component_storybook", require: "view_component/storybook/engine"
gem 'rack-mini-profiler', '< 3.0.0'
end

File diff suppressed because it is too large Load Diff

View File

@@ -35,17 +35,11 @@ We also have a [Super Admin Guide][super-admin-guide] to help with configuration
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!
We use [KnapsackPro](https://knapsackpro.com/) for optimal parallelisation of our automated tests. KnapsackPro offers unlimited plans for non-commercial open source projects, like ours - a big thanks to them!
![image](https://user-images.githubusercontent.com/49817236/201330047-e64147a7-d91c-4c10-bd4d-ca519d8fe945.png)
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!
## Licence
Copyright (c) 2012 - 2022 Open Food Foundation, released under the AGPL licence.
Copyright (c) 2012 - 2021 Open Food Foundation, released under the AGPL licence.
[survey]: https://docs.google.com/a/eaterprises.com.au/forms/d/1zxR5vSiU9CigJ9cEaC8-eJLgYid8CR8er7PPH9Mc-30/edit#
[slack-invite]: https://join.slack.com/t/openfoodnetwork/shared_invite/zt-9sjkjdlu-r02kUMP1zbrTgUhZhYPF~A

View File

@@ -8,3 +8,6 @@ require_relative 'config/application'
Openfoodnetwork::Application.load_tasks
if !ENV['DISABLE_KNAPSACK'] && defined?(Knapsack)
Knapsack.load_tasks
end

View File

@@ -22,6 +22,24 @@
//= require angular-rails-templates
//= require lodash.underscore.js
// datetimepicker (fil, nb)
//= require flatpickr/dist/flatpickr.min
//= require flatpickr/dist/l10n/ar
//= require flatpickr/dist/l10n/cat
//= require flatpickr/dist/l10n/cy
//= require flatpickr/dist/l10n/de
//= require flatpickr/dist/l10n/es
//= require flatpickr/dist/l10n/fr
//= require flatpickr/dist/l10n/it
//= require flatpickr/dist/l10n/nl
//= require flatpickr/dist/l10n/pl
//= require flatpickr/dist/l10n/pt
//= require flatpickr/dist/l10n/ru
//= require flatpickr/dist/l10n/sv
//= require flatpickr/dist/l10n/tr
//= require shortcut-buttons-flatpickr/dist/shortcut-buttons-flatpickr.min
//= require flatpickr/dist/plugins/labelPlugin/labelPlugin
// spree
//= require admin/spree/spree
//= require admin/spree/spree-select2
@@ -87,8 +105,6 @@
//= 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

@@ -256,13 +256,12 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
$scope.packVariant = (product, variant) ->
if variant.hasOwnProperty("unit_value_with_description")
match = variant.unit_value_with_description.match(/^([\d\.\,]+(?= |$)|)( |)(.*)$/)
match = variant.unit_value_with_description.match(/^([\d\.]+(?= |$)|)( |)(.*)$/)
if match
product = BulkProducts.find product.id
variant.unit_value = parseFloat(match[1].replace(",", "."))
variant.unit_value = parseFloat(match[1])
variant.unit_value = null if isNaN(variant.unit_value)
if variant.unit_value && product.variant_unit_scale
variant.unit_value = parseFloat(window.bigDecimal.multiply(variant.unit_value, product.variant_unit_scale, 2))
variant.unit_value *= product.variant_unit_scale if variant.unit_value && product.variant_unit_scale
variant.unit_description = match[3]
$scope.incrementLimit = ->

View File

@@ -39,9 +39,9 @@ angular.module("ofn.admin").directive "ofnDisplayAs", (OptionValueNamer) ->
# get relevant variant properties
variant = scope.$eval(attrs.ofnDisplayAs) # Like this so we can switch between 'master' and 'variant'
if variant.unit_value_with_description?
match = variant.unit_value_with_description.match(/^([\d\.\,]+(?= |$)|)( |)(.*)$/)
match = variant.unit_value_with_description.match(/^([\d\.]+(?= |$)|)( |)(.*)$/)
if match
unit_value = parseFloat(match[1].replace(",", "."))
unit_value = parseFloat(match[1])
unit_value = null if isNaN(unit_value)
unit_value *= variant_unit_scale if unit_value && variant_unit_scale
unit_description = match[3]

View File

@@ -10,7 +10,6 @@
scope.$emit "offClick"
element.click (event) ->
return if event.target.closest(".ofn-drop-down").classList.contains "disabled" || event.target.classList.contains "disabled"
if !scope.expanded
event.stopPropagation()
scope.deregistrationCallback = scope.$on "offClick", ->

View File

@@ -0,0 +1,15 @@
angular.module("admin.enterprise_groups")
.controller "sideMenuCtrl", ($scope, SideMenu) ->
$scope.menu = SideMenu
$scope.select = SideMenu.select
$scope.menu.setItems [
{ name: 'primary_details', label: t('primary_details'), icon_class: "icon-user" }
{ name: 'users', label: t('users'), icon_class: "icon-user" }
{ name: 'about', label: t('about'), icon_class: "icon-pencil" }
{ name: 'images', label: t('images'), icon_class: "icon-picture" }
{ name: 'contact', label: t('admin_enterprise_groups_contact'), icon_class: "icon-phone" }
{ name: 'web', label: t('admin_enterprise_groups_web'), icon_class: "icon-globe" }
]
$scope.select(0)

View File

@@ -0,0 +1,20 @@
# Used in enterprise new and edit forms to reset the state when the country is changed
angular.module("admin.enterprises").controller 'countryCtrl', ($scope, $timeout, availableCountries) ->
$scope.address_type = "address"
$scope.countries = availableCountries
$scope.countriesById = $scope.countries.reduce (obj, country) ->
obj[country.id] = country
obj
, {}
$timeout ->
$scope.$watch 'Enterprise.' + $scope.address_type + '.country_id', (newID, oldID) ->
$scope.clearState() unless $scope.addressStateMatchesCountry()
$scope.clearState = ->
$scope.Enterprise[$scope.address_type].state_id = null
$scope.addressStateMatchesCountry = ->
$scope.countriesById[$scope.Enterprise[$scope.address_type].country_id].states.some (state) ->
state.id == $scope.Enterprise[$scope.address_type].state_id

View File

@@ -1,7 +1,9 @@
angular.module("admin.enterprises")
.controller "enterpriseCtrl", ($scope, $http, $window, NavigationCheck, enterprise, Enterprises, SideMenu, StatusMessage, RequestMonitor) ->
.controller "enterpriseCtrl", ($scope, $http, $window, NavigationCheck, enterprise, Enterprises, EnterprisePaymentMethods, EnterpriseShippingMethods, SideMenu, StatusMessage, RequestMonitor) ->
$scope.Enterprise = enterprise
$scope.Enterprises = Enterprises
$scope.PaymentMethods = EnterprisePaymentMethods.paymentMethods
$scope.ShippingMethods = EnterpriseShippingMethods.shippingMethods
$scope.navClear = NavigationCheck.clear
$scope.menu = SideMenu
$scope.newManager = { id: null, email: (t('add_manager')) }

View File

@@ -0,0 +1,27 @@
angular.module("admin.enterprises")
.controller "permalinkCtrl", ($scope, PermalinkChecker) ->
# locals
initialPermalink = $scope.Enterprise.permalink
pendingRequest = null
# variables on $scope
$scope.availablility = ""
$scope.checking = false
$scope.$watch "Enterprise.permalink", (newValue, oldValue) ->
if newValue == initialPermalink
$scope.availability = ""
return
$scope.checking = true
pendingRequest = PermalinkChecker.check(newValue)
pendingRequest.then (data) ->
if data.permalink == initialPermalink
$scope.availability = ""
else
$scope.availability = data.available
$scope.Enterprise.permalink = data.permalink
$scope.checking = false
, (data) ->
# Do nothing (this is hopefully an aborted request)

View File

@@ -0,0 +1,49 @@
angular.module("admin.enterprises")
.controller "sideMenuCtrl", ($scope, $parse, enterprise, SideMenu, enterprisePermissions) ->
$scope.Enterprise = enterprise
$scope.menu = SideMenu
$scope.select = SideMenu.select
$scope.menu.setItems [
{ name: 'primary_details', label: t('primary_details'), icon_class: "icon-home" }
{ name: 'address', label: t('address'), icon_class: "icon-map-marker" }
{ name: 'contact', label: t('contact'), icon_class: "icon-phone" }
{ name: 'social', label: t('social'), icon_class: "icon-twitter" }
{ name: 'about', label: t('about'), icon_class: "icon-pencil" }
{ name: 'business_details', label: t('business_details'), icon_class: "icon-briefcase" }
{ name: 'images', label: t('images'), icon_class: "icon-picture" }
{ name: 'properties', label: t('properties'), icon_class: "icon-tags", show: "showProperties()" }
{ name: 'shipping_methods', label: t('shipping_methods'), icon_class: "icon-truck", show: "showShippingMethods()" }
{ name: 'payment_methods', label: t('payment_methods'), icon_class: "icon-money", show: "showPaymentMethods()" }
{ name: 'enterprise_fees', label: t('enterprise_fees'), icon_class: "icon-tasks", show: "showEnterpriseFees()" }
{ name: 'enterprise_permissions', label: t('enterprise_permissions'), icon_class: "icon-plug" }
{ name: 'inventory_settings', label: t('inventory_settings'), icon_class: "icon-list-ol", show: "enterpriseIsShop()" }
{ name: 'tag_rules', label: t('tag_rules'), icon_class: "icon-random", show: "enterpriseIsShop()" }
{ name: 'shop_preferences', label: t('shop_preferences'), icon_class: "icon-shopping-cart", show: "enterpriseIsShop()" }
{ name: 'users', label: t('users'), icon_class: "icon-user" }
]
SideMenu.init()
$scope.showItem = (item) ->
if item.show?
$parse(item.show)($scope)
else
true
$scope.showProperties = ->
!!$scope.Enterprise.is_primary_producer
$scope.showShippingMethods = ->
enterprisePermissions.can_manage_shipping_methods && $scope.Enterprise.sells != "none"
$scope.showPaymentMethods = ->
enterprisePermissions.can_manage_payment_methods && $scope.Enterprise.sells != "none"
$scope.showEnterpriseFees = ->
enterprisePermissions.can_manage_enterprise_fees && ($scope.Enterprise.sells != "none" || $scope.Enterprise.is_primary_producer)
$scope.enterpriseIsShop = ->
$scope.Enterprise.sells != "none"
$scope.menu.redirect_function('enterprise_permissions', '/admin/enterprise_relationships')

View File

@@ -0,0 +1,20 @@
angular.module("admin.enterprises")
.factory "EnterprisePaymentMethods", (enterprise, PaymentMethods) ->
new class EnterprisePaymentMethods
paymentMethods: PaymentMethods.all
constructor: ->
for payment_method in @paymentMethods
payment_method.selected = payment_method.id in enterprise.payment_method_ids
displayColor: ->
if @paymentMethods.length > 0 && @selectedCount() > 0
"blue"
else
"red"
selectedCount: ->
@paymentMethods.reduce (count, payment_method) ->
count++ if payment_method.selected
count
, 0

View File

@@ -0,0 +1,20 @@
angular.module("admin.enterprises")
.factory "EnterpriseShippingMethods", (enterprise, ShippingMethods) ->
new class EnterpriseShippingMethods
shippingMethods: ShippingMethods.all
constructor: ->
for shipping_method in @shippingMethods
shipping_method.selected = shipping_method.id in enterprise.shipping_method_ids
displayColor: ->
if @shippingMethods.length > 0 && @selectedCount() > 0
"blue"
else
"red"
selectedCount: ->
@shippingMethods.reduce (count, shipping_method) ->
count++ if shipping_method.selected
count
, 0

View File

@@ -0,0 +1,12 @@
angular.module("admin.indexUtils").component 'showMore',
templateUrl: 'admin/show_more.html'
bindings:
data: "="
limit: "="
increment: "="
# For now, this component is not being used.
# Something about binding "data" to a variable on the parent scope that is continually refreshed by
# being assigned within an ng-repeat means that we get $digest iteration errors. Seems to be solved
# by using the new "as" syntax for ng-repeat to assign and alias the outcome of the filters, but this
# has the limitation of not being able to be limited AFTER the assignment has been made, which we need

View File

@@ -3,21 +3,14 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.RequestMonitor = RequestMonitor
$scope.line_items = LineItems.all
$scope.confirmDelete = true
$scope.startDate = moment().startOf('day').subtract(7, 'days').format('YYYY-MM-DD')
$scope.endDate = moment().startOf('day').format('YYYY-MM-DD')
$scope.bulkActions = [ { name: t("admin.orders.bulk_management.actions_delete"), callback: 'deleteLineItems' } ]
$scope.selectedUnitsProduct = {}
$scope.selectedUnitsVariant = {}
$scope.sharedResource = false
$scope.columns = Columns.columns
$scope.sorting = SortOptions
$scope.pagination = Orders.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.confirmRefresh = ->
LineItems.allSaved() || confirm(t("unsaved_changes_warning"))
@@ -27,22 +20,19 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.supplierFilter = ''
$scope.orderCycleFilter = ''
$scope.quickSearch = ''
$scope.startDate = undefined
$scope.endDate = undefined
event = new CustomEvent('flatpickr:clear')
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 = ->
return "cancel" unless $scope.confirmRefresh()
unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == ''
$scope.setOrderCycleDateRange()
$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()
$scope.loadOrders()
$scope.loadLineItems()
@@ -52,24 +42,25 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.dereferenceLoadedData()
$scope.loadOrders = ->
[formattedStartDate, formattedEndDate] = $scope.formatDates($scope.startDate, $scope.endDate)
$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')
$scope.loadOrders = ->
RequestMonitor.load $scope.orders = Orders.index(
"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]": if formattedStartDate then formattedStartDate else undefined,
"q[completed_at_lt]": if formattedEndDate then formattedEndDate else undefined,
"page": $scope.page,
"per_page": $scope.per_page
"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[order_state_not_eq]": "canceled",
"q[order_shipment_state_not_eq]": "shipped",
@@ -77,17 +68,10 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
"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,
"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()}")
@@ -182,18 +166,12 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.sumUnitValues = ->
sum = $scope.filteredLineItems?.reduce (sum, lineItem) ->
if lineItem.units_product.variant_unit == "items"
sum + lineItem.quantity
else
sum + $scope.roundToThreeDecimals(lineItem.final_weight_volume / $scope.getLineItemScale(lineItem))
sum + $scope.roundToThreeDecimals(lineItem.final_weight_volume / $scope.getLineItemScale(lineItem))
, 0
$scope.sumMaxUnitValues = ->
sum = $scope.filteredLineItems?.reduce (sum,lineItem) ->
if lineItem.units_product.variant_unit == "items"
sum + lineItem.max_quantity
else
sum + lineItem.max_quantity * $scope.roundToThreeDecimals(lineItem.units_variant.unit_value / $scope.getLineItemScale(lineItem))
sum + lineItem.max_quantity * $scope.roundToThreeDecimals(lineItem.units_variant.unit_value / $scope.getLineItemScale(lineItem))
, 0
$scope.roundToThreeDecimals = (value) ->
@@ -207,8 +185,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.getScale = (unitsProduct, unitsVariant) ->
if unitsProduct.hasOwnProperty("variant_unit") && (unitsProduct.variant_unit == "weight" || unitsProduct.variant_unit == "volume")
unitsProduct.variant_unit_scale
else if unitsProduct.hasOwnProperty("variant_unit") && unitsProduct.variant_unit == "items"
1
else
null
@@ -218,7 +194,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
@@ -233,11 +209,11 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.fulfilled = (sumOfUnitValues) ->
# A Units Variant is an API object which holds unit properies of a variant
if $scope.selectedUnitsProduct.hasOwnProperty("group_buy_unit_size")&& $scope.selectedUnitsProduct.group_buy_unit_size > 0 &&
$scope.selectedUnitsProduct.hasOwnProperty("variant_unit")
if $scope.selectedUnitsProduct.variant_unit == "weight" || $scope.selectedUnitsProduct.variant_unit == "volume"
scale = $scope.selectedUnitsProduct.variant_unit_scale
sumOfUnitValues = sumOfUnitValues * scale unless scale == 28.35 || scale == 453.6
if $scope.selectedUnitsProduct.hasOwnProperty("group_buy_unit_size") && $scope.selectedUnitsProduct.group_buy_unit_size > 0 &&
$scope.selectedUnitsProduct.hasOwnProperty("variant_unit") &&
( $scope.selectedUnitsProduct.variant_unit == "weight" || $scope.selectedUnitsProduct.variant_unit == "volume" )
scale = $scope.selectedUnitsProduct.variant_unit_scale
sumOfUnitValues = sumOfUnitValues * scale unless scale == 28.35 || scale == 453.6
$scope.roundToThreeDecimals(sumOfUnitValues / $scope.selectedUnitsProduct.group_buy_unit_size)
else
''
@@ -262,8 +238,5 @@ 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()
$scope.resetFilters()
$scope.refreshData()

View File

@@ -38,4 +38,3 @@ angular.module('admin.orderCycles')
$scope.removeCoordinatorFee = ($event, index) ->
$event.preventDefault()
OrderCycle.removeCoordinatorFee(index)
$scope.order_cycle_form.$dirty = true

View File

@@ -18,8 +18,7 @@ angular.module('admin.orderCycles')
OrderCycle.exchangeDirection(exchange)
$scope.enterprisesWithFees = ->
ids = [OrderCycle.participatingEnterpriseIds()..., [OrderCycle.order_cycle.coordinator_id]...]
$scope.enterprises[id] for id in Array.from(new Set(ids)) when $scope.enterpriseFeesForEnterprise(id).length > 0
$scope.enterprises[id] for id in [OrderCycle.participatingEnterpriseIds()..., [OrderCycle.order_cycle.coordinator_id]...] when $scope.enterpriseFeesForEnterprise(id).length > 0
$scope.removeExchange = ($event, exchange) ->
$event.preventDefault()

View File

@@ -1,4 +1,21 @@
angular.module('admin.orderCycles', ['ngTagsInput', 'admin.indexUtils', 'admin.enterprises'])
.directive 'datetimepicker', ($timeout, $parse) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
$timeout ->
fp = flatpickr(element, Object.assign({},
window.FLATPICKR_DATETIME_DEFAULT, {
onOpen: (selectedDates, dateStr, instance) ->
instance.setDate(ngModel.$modelValue)
instance.input.dispatchEvent(new Event('focus', { bubbles: true }));
}));
fp.minuteElement.addEventListener "keyup", (e) ->
if !isNaN(event.target.value)
fp.setDate(fp.selectedDates[0].setMinutes(e.target.value), true)
fp.hourElement.addEventListener "keyup", (e) ->
if !isNaN(event.target.value)
fp.setDate(fp.selectedDates[0].setHours(e.target.value), true)
.directive 'ofnOnChange', ->
(scope, element, attrs) ->
element.bind 'change', ->

View File

@@ -93,9 +93,9 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $
variant_ids
participatingEnterpriseIds: ->
suppliers = (parseInt(exchange.enterprise_id) for exchange in this.order_cycle.incoming_exchanges)
distributors = (parseInt(exchange.enterprise_id) for exchange in this.order_cycle.outgoing_exchanges)
Array.from(new Set([suppliers..., distributors...]))
suppliers = (exchange.enterprise_id for exchange in this.order_cycle.incoming_exchanges)
distributors = (exchange.enterprise_id for exchange in this.order_cycle.outgoing_exchanges)
jQuery.unique(suppliers.concat(distributors)).sort()
exchangesByDirection: (direction) ->
if direction == 'incoming'

View File

@@ -1,14 +0,0 @@
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

@@ -20,7 +20,7 @@ angular.module("admin.orders").controller "orderCtrl", ($scope, shops, orderCycl
$scope.distributor_id && $scope.order_cycle_id
for oc in $scope.orderCycles
oc.name_and_status = "#{oc.name} (#{t("admin.order_cycles.status.#{oc.status}")})"
oc.name_and_status = "#{oc.name} (#{oc.status})"
for shop in $scope.shops
shop.disabled = !$scope.distributorHasOrderCycles(shop)

View File

@@ -29,8 +29,6 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, Reque
$scope.q = {
completed_at_not_null: true
}
e = new CustomEvent("flatpickr_clear");
window.dispatchEvent(e)
$scope.clearFilters = () ->
KeyValueMapStore.clearKeyValueMap()

View File

@@ -0,0 +1,62 @@
angular.module("admin.orders").directive 'customerSearchOverride', ->
restrict: 'C'
scope:
distributorId: '@'
link: (scope, element, attr) ->
if $('#customer_autocomplete_template').length > 0
customerTemplate = Handlebars.compile($('#customer_autocomplete_template').text())
formatCustomerResult = (customer) ->
customerTemplate
customer: customer
bill_address: customer.bill_address
ship_address: customer.ship_address
element.select2
placeholder: Spree.translations.choose_a_customer
minimumInputLength: 3
ajax:
url: '/admin/search/customers.json'
datatype: 'json'
data: (term, page) ->
{
q: term
distributor_id: scope.distributorId # modified
}
results: (data, page) ->
{ results: data }
dropdownCssClass: 'customer_search'
formatResult: formatCustomerResult
formatSelection: (customer) ->
_.each [
'bill_address'
'ship_address'
], (address) ->
data = customer[address]
address_parts = [
'firstname'
'lastname'
'company'
'address1'
'address2'
'city'
'zipcode'
'phone'
]
attribute_wrapper = '#order_' + address + '_attributes_'
if data # modified
_.each address_parts, (part) ->
$(attribute_wrapper + part).val data[part]
return
$(attribute_wrapper + 'state_id').select2 'val', data['state_id']
$(attribute_wrapper + 'country_id').select2 'val', data['country_id']
else
_.each address_parts, (part) ->
$(attribute_wrapper + part).val ''
return
$(attribute_wrapper + 'state_id').select2 'val', ''
$(attribute_wrapper + 'country_id').select2 'val', ''
return
$('#order_email').val customer.email
$('#user_id').val customer.user_id # modified
customer.email

View File

@@ -0,0 +1,3 @@
angular.module("admin.paymentMethods").controller "paymentMethodsCtrl", ($scope, PaymentMethods) ->
$scope.findPaymentMethodByID = (id) ->
$scope.PaymentMethod = PaymentMethods.byID[id]

View File

@@ -8,7 +8,7 @@ angular.module("admin.products")
$scope.processVariantUnitWithScale()
$scope.processUnitValueWithDescription()
$scope.processUnitPrice()
$scope.placeholder_text = new OptionValueNamer($scope.product.master).name() if $scope.product.variant_unit_scale
$scope.placeholder_text = new OptionValueNamer($scope.product.master).name()
$scope.variant_unit_options = VariantUnitManager.variantUnitOptions()
@@ -21,14 +21,6 @@ angular.module("admin.products")
else
$scope.product.variant_unit = $scope.product.variant_unit_with_scale
$scope.product.variant_unit_scale = null
else if $scope.product.variant_unit
# Preserves variant_unit_with_scale when form validation fails and reload triggers
if $scope.product.variant_unit_scale
$scope.product.variant_unit_with_scale = VariantUnitManager.getUnitWithScale(
$scope.product.variant_unit, parseFloat($scope.product.variant_unit_scale)
)
else
$scope.product.variant_unit_with_scale = $scope.product.variant_unit
else
$scope.product.variant_unit = $scope.product.variant_unit_scale = null
@@ -38,12 +30,8 @@ angular.module("admin.products")
if match
$scope.product.master.unit_value = PriceParser.parse(match[1])
$scope.product.master.unit_value = null if isNaN($scope.product.master.unit_value)
$scope.product.master.unit_value = window.bigDecimal.multiply($scope.product.master.unit_value, $scope.product.variant_unit_scale, 2) if $scope.product.master.unit_value && $scope.product.variant_unit_scale
$scope.product.master.unit_value *= $scope.product.variant_unit_scale if $scope.product.master.unit_value && $scope.product.variant_unit_scale
$scope.product.master.unit_description = match[3]
else
value = $scope.product.master.unit_value
value = window.bigDecimal.divide(value, $scope.product.variant_unit_scale, 2) if $scope.product.master.unit_value && $scope.product.variant_unit_scale
$scope.product.master.unit_value_with_description = value + " " + $scope.product.master.unit_description
$scope.processUnitPrice = ->
price = $scope.product.price

View File

@@ -23,10 +23,10 @@ angular.module("admin.products").controller "variantUnitsCtrl", ($scope, Variant
$scope.updateValue = ->
unit_value_human = angular.element('#unit_value_human').val()
$scope.unit_value = bigDecimal.multiply(PriceParser.parse(unit_value_human), $scope.scale, 2)
$scope.unit_value = PriceParser.parse(unit_value_human) * $scope.scale
variant_unit_value = angular.element('#variant_unit_value').val()
$scope.unit_value_human = parseFloat(bigDecimal.divide(variant_unit_value, $scope.scale, 2))
$scope.unit_value_human = variant_unit_value / $scope.scale
$timeout -> $scope.processUnitPrice()
$timeout -> $scope.updateValue()

View File

@@ -13,9 +13,3 @@ angular.module("ofn.admin").factory "ProductImageService", (FileUploader, SpreeA
@imageUploader.onSuccessItem = (image, response) =>
product.thumb_url = response.thumb_url
product.image_url = response.image_url
@imageUploader.onErrorItem = (image, response) =>
if Array.isArray(response.errors)
message = response.errors.join("\n")
else
message = response.error.toString()
alert(message)

View File

@@ -27,22 +27,16 @@ angular.module("admin.products").factory "VariantUnitManager", (availableUnits)
1000.0:
name: 'kL'
system: 'metric'
'items':
1:
name: 'items'
@variantUnitOptions: ->
available = availableUnits.split(",")
options = for unit_type, _ of @units
for scale in @unitScales(unit_type, available)
name = @getUnitName(scale, unit_type)
["#{I18n.t(unit_type)} (#{name})", @getUnitWithScale(unit_type, scale)]
["#{I18n.t(unit_type)} (#{name})", "#{unit_type}_#{scale}"]
options.push [[I18n.t('items'), 'items']]
options = [].concat options...
@getUnitWithScale: (unit_type, scale) ->
"#{unit_type}_#{scale}"
@getUnitName: (scale, unitType) ->
if @units[unitType][scale]
@units[unitType][scale]['name']

View File

@@ -65,8 +65,7 @@ angular.module("ofn.admin").factory "BulkProducts", (ProductResource, dataFetche
variantUnitValue: (product, variant) ->
if variant.unit_value?
if product.variant_unit_scale
variant_unit_value = @divideAsInteger variant.unit_value, product.variant_unit_scale
parseFloat(window.bigDecimal.round(variant_unit_value, 2))
@divideAsInteger variant.unit_value, product.variant_unit_scale
else
variant.unit_value
else

View File

@@ -0,0 +1,3 @@
angular.module("admin.shippingMethods").controller "shippingMethodsCtrl", ($scope, ShippingMethods) ->
$scope.findShippingMethodByID = (id) ->
$scope.ShippingMethod = ShippingMethods.byID[id]

View File

@@ -25,12 +25,13 @@ $(document).ready(function() {
var link = $(this);
var shipment_number = link.data('shipment-number');
var selected_shipping_rate_id = link.parents('tbody').find("select#selected_shipping_rate_id[data-shipment-number='" + shipment_number + "']").val();
var unlock = link.parents('tbody').find("input[name='open_adjustment'][data-shipment-number='" + shipment_number + "']:checked").val();
var url = Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number + ".json");
$.ajax({
type: "PUT",
url: url,
data: { shipment: { selected_shipping_rate_id: selected_shipping_rate_id } }
data: { shipment: { selected_shipping_rate_id: selected_shipping_rate_id, unlock: unlock } }
}).done(function( msg ) {
window.location.reload();
}).error(function( msg ) {
@@ -39,94 +40,25 @@ $(document).ready(function() {
}
$('[data-hook=admin_order_edit_form] a.save-method').click(handle_shipping_method_save);
//handle tracking info edit/delete
// Show the input field to edit the tracking info
// And hide the input field when cancel is clicked
//handle tracking edit click
$('a.edit-tracking').click(toggleTrackingEdit);
$('a.cancel-tracking').click(toggleTrackingEdit);
saveTrackingInfo = function(){
let shipmentNumber = $(this).data('shipment-number');
let tracking = document.getElementById('tracking').value
handle_tracking_save = function(){
var link = $(this);
var shipment_number = link.data('shipment-number');
var tracking = link.parents('tbody').find('input#tracking').val();
var url = Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number + ".json");
makeApiCall(trackingUrl(shipmentNumber), { shipment: { tracking: tracking } } )
}
deleteTrackingInfo = function(){
let shipmentNumber = $(this).data('shipment-number');
let tracking = ''
confirmDelete(trackingUrl(shipmentNumber), { shipment: { tracking: tracking } })
}
trackingUrl = function(shipmentNumber){
return Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipmentNumber + ".json");
}
$('[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
// Show the input field to edit the note
// And hide the input field when cancel is clicked
$('a.edit-note.icon-edit').click(toggleNoteEdit);
$('a.cancel-note').click(toggleNoteEdit);
saveNote = function(){
let note = document.getElementById('note').value
makeApiCall(getNoteUrl(), { note: note })
}
deleteNote = function(){
let note = ''
confirmDelete(getNoteUrl(), { note: note })
}
getNoteUrl = function(){
return Spree.url( Spree.routes.orders_api + "/" + order_number);
}
confirmDelete = function(url, params){
displayDeleteAlert(function(confirmation) {
if (confirmation) {
makeApiCall(url, params)
}
});
}
$('[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) {
$.ajax({
type: "PUT",
url: url,
data: params
data: { shipment: { tracking: tracking } }
}).done(function( msg ) {
window.location.reload();
}).error(function( msg ) {
console.log(msg);
});
}
displayDeleteAlert = function(callback) {
i18nKey = "are_you_sure";
$('#custom-confirm .message').html(
` ${t(i18nKey)}
<div class="form">
</div>`);
$('#custom-confirm button.confirm').unbind( "click" ).click(() => {
$('#custom-confirm').hide();
callback(true);
});
$('#custom-confirm button.cancel').click(() => {
$('#custom-confirm').hide();
callback(false)
});
$('#custom-confirm').show();
}
$('[data-hook=admin_order_edit_form] a.save-tracking').click(handle_tracking_save);
});

View File

@@ -141,26 +141,8 @@ doAdjustItems = function(shipment_number, variant_id, quantity, inventory_units,
toggleTrackingEdit = function(){
var link = $(this);
var parent_node = link.parents('tbody')
let input = parent_node.find('#tracking')[0]
parent_node.find('tr.edit-tracking').toggle();
// Set focus on input and
// put cursor at it's end
input.focus()
input.setSelectionRange(-1, -1)
parent_node.find('tr.show-tracking').toggle();
}
toggleNoteEdit = function(){
var link = $(this);
var parent_node = link.parents('tbody')
let input = parent_node.find('#note')[0]
parent_node.find('tr.edit-note').toggle();
// Set focus on input and
// put cursor at it's end
input.focus()
input.setSelectionRange(-1, -1)
parent_node.find('tr.show-note').toggle();
link.parents('tbody').find('tr.edit-tracking').toggle();
link.parents('tbody').find('tr.show-tracking').toggle();
}
toggleMethodEdit = function(){

View File

@@ -8,18 +8,13 @@ handle_move = (e, data) ->
node = data.rslt.o
new_parent = data.rslt.np
url = new URL(base_url)
url.pathname = url.pathname + '/' + node.attr("id")
data = {
_method: "put",
"taxon[position]": position,
"taxon[parent_id]": if !isNaN(new_parent.attr("id")) then new_parent.attr("id") else undefined
}
url = Spree.url(base_url).clone()
url.pathname = url.pathname + '/' + node.attr("id")
$.ajax
type: "POST",
dataType: "json",
url: url.toString(),
data: data,
data: ({_method: "put", "taxon[parent_id]": new_parent.attr("id"), "taxon[position]": position }),
error: handle_ajax_error
true
@@ -31,16 +26,11 @@ handle_create = (e, data) ->
position = data.rslt.position
new_parent = data.rslt.parent
data = {
"taxon[name]": name,
"taxon[position]": position
"taxon[parent_id]": if !isNaN(new_parent.attr("id")) then new_parent.attr("id") else undefined
}
$.ajax
type: "POST",
dataType: "json",
url: base_url.toString(),
data: data,
data: ({"taxon[name]": name, "taxon[parent_id]": new_parent.attr("id"), "taxon[position]": position }),
error: handle_ajax_error,
success: (data,result) ->
node.attr('id', data.id)
@@ -49,10 +39,8 @@ handle_rename = (e, data) ->
last_rollback = data.rlbk
node = data.rslt.obj
name = data.rslt.new_name
# change the name inside the main input field as well if taxon is the root one
document.getElementById("taxonomy_name").value = name if node.parents("[id]").attr("id") == "taxonomy_tree"
url = new URL(base_url)
url = Spree.url(base_url).clone()
url.pathname = url.pathname + '/' + node.attr("id")
$.ajax
@@ -65,8 +53,8 @@ handle_rename = (e, data) ->
handle_delete = (e, data) ->
last_rollback = data.rlbk
node = data.rslt.obj
delete_url = new URL(base_url)
delete_url.pathname = delete_url.pathname + '/' + node.attr("id")
delete_url = base_url.clone()
delete_url.setPath delete_url.path() + '/' + node.attr("id")
if confirm(Spree.translations.are_you_sure_delete)
$.ajax
type: "POST",

View File

@@ -0,0 +1,29 @@
angular.module("admin.subscriptions").directive 'newSubscriptionDialog', ($rootScope, $compile, $window, $templateCache, DialogDefaults, shops) ->
restrict: 'A'
scope: true
link: (scope, element, attr) ->
scope.submitted = false
scope.shops = shops
scope.shop_id = null
scope.newSubscription = ->
scope.new_subscription_form.$setPristine()
scope.submitted = true
if scope.shop_id?
$window.location.href = "/admin/subscriptions/new?subscription[shop_id]=#{scope.shop_id}"
return
# Compile modal template
template = $compile($templateCache.get('admin/new_subscription_dialog.html'))(scope)
# Set Dialog options
template.dialog(DialogDefaults)
# Link opening of dialog to click event on element
element.bind 'click', (e) ->
if shops.length == 1
scope.shop_id = shops[0].id
scope.newSubscription()
else
template.dialog('open')
$rootScope.$evalAsync()

View File

@@ -1,4 +1,70 @@
$(document).ready(function() {
var onClickButtons = function(index, fp) {
var date;
// Memorize index used for the 'Close' button
// (currently it has index of 1)
var closeButtonIndex = 1;
switch (index) {
case 0:
date = new Date();
break;
case closeButtonIndex:
fp.close();
break;
}
// Set the date unless clicked button was the 'Close' one
if (index != closeButtonIndex) {
fp.setDate(date, true);
}
}
window.FLATPICKR_DATE_DEFAULT = {
altInput: true,
altFormat: Spree.translations.flatpickr_date_format,
dateFormat: "Y-m-d",
locale: I18n.base_locale,
plugins: [
ShortcutButtonsPlugin({
button: [
{
label: Spree.translations.today
},
{
label: Spree.translations.close
}
],
label: "or",
onClick: onClickButtons
}),
labelPlugin({})
]
}
window.FLATPICKR_DATETIME_DEFAULT = Object.assign(
{},
window.FLATPICKR_DATE_DEFAULT,
{
altInput: true,
altFormat: Spree.translations.flatpickr_datetime_format,
dateFormat: "Y-m-d H:i",
enableTime: true,
time_24hr: true,
plugins: [
ShortcutButtonsPlugin({
button: [
{
label: Spree.translations.now
},
{
label: Spree.translations.close
}
],
label: "or",
onClick: onClickButtons
}),
labelPlugin({})
]
}
);
flatpickr(".datetimepicker", window.FLATPICKR_DATETIME_DEFAULT);
$('a.close').click(function(event){
event.preventDefault();
$(this).parent().slideUp(250);

View File

@@ -0,0 +1,14 @@
angular.module("admin.utils").directive "datepicker", ($window, $timeout) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
$timeout ->
flapickrInstance = flatpickr(element, Object.assign(
{},
$window.FLATPICKR_DATE_DEFAULT, {
onOpen: (selectedDates, dateStr, instance) ->
instance.setDate(ngModel.$modelValue)
}
));
ngModel.$render = () ->
newValue = ngModel.$viewValue;
flapickrInstance?.setDate(newValue)

View File

@@ -3,7 +3,6 @@ angular.module("admin.utils").directive "tagsWithTranslation", ($timeout) ->
templateUrl: "admin/tags_input.html"
scope:
object: "="
form: "="
tagsAttr: "@?"
tagListAttr: "@?"
findTags: "&"
@@ -19,15 +18,7 @@ angular.module("admin.utils").directive "tagsWithTranslation", ($timeout) ->
scope.limitReached = scope.object[scope.tagsAttr].length >= scope.max if scope.max != undefined
scope.object[scope.tagListAttr] = (tag.text for tag in scope.object[scope.tagsAttr]).join(",")
scope.$watch "object", (newObject) ->
scope.object = newObject
init()
$timeout ->
init()
init = ->
return unless scope.object
# Initialize properties if necessary
scope.tagsAttr ||= "tags"
scope.tagListAttr ||= "tag_list"

View File

@@ -5,16 +5,15 @@ angular.module("admin.utils").factory "ErrorsParser", ->
return defaultContent unless errors?
errorsString = ""
if Array.isArray(errors)
if errors.length > 0
# it is an array of errors
errorsString = errors.join("\n") + "\n"
else if typeof errors == "object"
else
# it is a hash of errors
keys = Object.keys(errors)
for key in keys
errorsString += errors[key].join("\n") + "\n"
else # string
errorsString = errors
this.defaultIfEmpty(errorsString, defaultContent)
defaultIfEmpty: (content, defaultContent) =>

View File

@@ -20,6 +20,3 @@ angular.module('Darkswarm').controller "CreditCardsCtrl", ($scope, $http, Credit
).finally ->
window.location.reload()
$scope.hasOneDefaultSavedCards = () ->
$scope.savedCreditCards.some((card) -> card.is_default)

View File

@@ -0,0 +1,9 @@
angular.module('Darkswarm').directive "fillVertical", ($window)->
# Makes something fill the window vertically. Used on the Google Map.
restrict: 'A'
link: (scope, element, attrs)->
setSize = ->
element.css "height", ($window.innerHeight - element.offset().top)
setSize()
angular.element($window).bind "resize", ->
setSize()

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

@@ -26,8 +26,4 @@ angular.module('mm.foundation.offcanvas').directive 'offCanvasWrap', ($window) -
# Bind hiding of the off-canvas that only happens when screen width is over 1024px.
win.bind 'resize.body', ->
isolatedScope.hide() if $(window).width() > 1024
win.bind 'click.body', (e) ->
if e.target.closest(".left-off-canvas-menu") == null && e.target.closest(".left-off-canvas-toggle") == null
isolatedScope.hide()
}

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

@@ -0,0 +1,82 @@
angular.module('Darkswarm').directive 'singleLineSelectors', ($timeout, $filter) ->
restrict: 'E'
templateUrl: "single_line_selectors.html"
scope:
selectors: "="
objects: "&"
activeSelectors: "="
selectorName: "@activeSelectors"
link: (scope, element, attrs) ->
scope.fitting = false
scope.refit = ->
if scope.allSelectors?
scope.fitting = true
selector.fits = true for selector in scope.allSelectors
$timeout(loadWidths, 0, true).then ->
$timeout fit, 0, true
fit = ->
used = $(element).find("li.more").outerWidth(true)
used += selector.width for selector in scope.allSelectors when selector.fits
available = $(element).parent(".filter-shopfront").innerWidth() - used
if available > 0
for selector in scope.allSelectors when !selector.fits
available -= selector.width
selector.fits = true if available > 0
else
if scope.allSelectors.length > 0
for i in [scope.allSelectors.length-1..0]
selector = scope.allSelectors[i]
if !selector.fits
continue
else
if available < 0
selector.fits = false
available += selector.width
scope.fitting = false
loadWidths = ->
$(element).find("li").not(".more").each (i) ->
if i < scope.allSelectors.length
scope.allSelectors[i].width = $(this).outerWidth(true)
return null # So we don't exit the loop weirdly
scope.overFlowSelectors = ->
return [] unless scope.allSelectors?
$filter('filter')(scope.allSelectors, { fits: false })
scope.selectedOverFlowSelectors = ->
$filter('filter')(scope.overFlowSelectors(), { active: true })
# had to duplicate this to make overflow selectors work
scope.emit = ->
scope.activeSelectors = scope.allSelectors.filter (selector)->
selector.active
.map (selector) ->
selector.object.id
# From: http://stackoverflow.com/questions/4298612/jquery-how-to-call-resize-event-only-once-its-finished-resizing
debouncer = (func, timeout) ->
timeoutID = undefined
timeout = timeout or 50
->
subject = this
args = arguments
clearTimeout timeoutID
timeoutID = setTimeout(->
func.apply subject, Array::slice.call(args)
, timeout)
# -- Event management
scope.$watchCollection "allSelectors", ->
scope.refit()
scope.$on "filtersToggled", ->
scope.refit()
$(window).resize debouncer (e) ->
scope.fitting = true
if scope.allSelectors?
$timeout fit, 0, true

View File

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

View File

@@ -0,0 +1,14 @@
#new-subscription-dialog
.text-normal.margin-bottom-30.text-center
= t('js.admin.subscriptions.new.please_select_a_shop')
%form{ name: 'new_subscription_form', novalidate: true, ng: { submit: "newSubscription()" }}
.text-center.margin-bottom-30
%input.ofn-select2.fullwidth#new_subscription_shop_id{ ng: { model: 'shop_id' }, required: true, name: 'shop_id', data: 'shops' }
%div{ ng: { show: "submitted && new_subscription_form.$pristine" } }
.error{ ng: { show: "new_subscription_form.shop_id.$error.required" } }
= t('js.admin.subscriptions.new.please_select_a_shop')
.text-center
%input.button.red.icon-plus{ type: 'submit', value: t('continue') }

View File

@@ -0,0 +1,3 @@
%div{ ng: { show: "data.length > limit" } }
%input{ type: 'button', value: t(:show_more), ng: { click: 'limit = limit + increment' } }
%input{ type: 'button', value: t(:show_all_with_more, num: '{{ data.length - limit }}'), ng: { click: 'limit = data.length' } }

View File

@@ -1,12 +1,8 @@
%div.contact-container
%div.modal-centered{"ng-if" => "::enterprise.email_address || enterprise.website || enterprise.phone || enterprise.whatsapp_phone"}
%div.modal-centered{"ng-if" => "::enterprise.email_address || enterprise.website || enterprise.phone"}
%p.modal-header {{'contact' | t}}
%p{"ng-if" => "::enterprise.phone", "ng-bind" => "::enterprise.phone"}
%p{"ng-if" => "::enterprise.whatsapp_phone"}
%img{ src: image_path("/map_icons/social-logos/whatsapp.svg") }
%a{"ng-href" => "{{::enterprise.whatsapp_url}}", target: "_blank", "ng-bind" => "::enterprise.whatsapp_phone"}
%p{"ng-if" => "::enterprise.email_address"}
%a{"ng-href" => "{{::enterprise.email_address | stripUrl}}", target: "_blank", mailto: true}
%span.obfuscatedEmail.email{"ng-bind" => "::enterprise.email_address | stripUrl"}

View File

@@ -9,19 +9,19 @@
%span{"ng-bind" => "::'item_cost' | t"}
%li{"ng-if" => "::variant.fees.admin"}
.right {{ ::variant.fees.admin | localizeCurrency }}
%span{"ng-bind" => "::variant.fees_name.admin"}
%span{"ng-bind" => "::'admin_fee' | t"}
%li{"ng-if" => "::variant.fees.sales"}
.right {{ ::variant.fees.sales | localizeCurrency }}
%span{"ng-bind" => "::variant.fees_name.sales"}
%span{"ng-bind" => "::'sales_fee' | t"}
%li{"ng-if" => "::variant.fees.packing"}
.right {{ ::variant.fees.packing | localizeCurrency }}
%span{"ng-bind" => "::variant.fees_name.packing"}
%span{"ng-bind" => "::'packing_fee' | t"}
%li{"ng-if" => "::variant.fees.transport"}
.right {{ ::variant.fees.transport | localizeCurrency }}
%span{"ng-bind" => "::variant.fees_name.transport"}
%span{"ng-bind" => "::'transport_fee' | t"}
%li{"ng-if" => "::variant.fees.fundraising"}
.right {{ ::variant.fees.fundraising | localizeCurrency }}
%span{"ng-bind" => "::variant.fees_name.fundraising"}
%span{"ng-bind" => "::'fundraising_fee' | t"}
%li
%strong
.right = {{ ::variant.price_with_fees | localizeCurrency }}

View File

@@ -0,0 +1,14 @@
-# In order for the single-line-selector scope to have access to the available selectors,
%filter-selector{"selector-set" => "selectors", objects: "objects()", "active-selectors" => "activeSelectors", "all-selectors" => "allSelectors" }
%ul{ ng: { if: "overFlowSelectors().length > 0 || fitting" } }
%li.more
%a.dropdown{ data: { dropdown: "{{ 'show-more-' + selectorName }}" }, ng: { class: "{active: selectedOverFlowSelectors().length > 0}" } }
%span
{{ 'js.more_items' | t:{ count: overFlowSelectors().length } }}
%i.ofn-i_052-point-down
.f-dropdown.text-right.content{ ng: { attr: { id: "{{ 'show-more-' + selectorName }}" } } }
%ul
%active-selector{ ng: { repeat: "selector in overFlowSelectors()", hide: "selector.fits" } }
%render-svg{path: "{{selector.object.icon}}", ng: { if: "selector.object.icon"}}
%span {{ selector.object.name }}

View File

@@ -1,6 +0,0 @@
# frozen_string_literal: true
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
end

View File

@@ -1,24 +0,0 @@
# frozen_string_literal: true
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :session_id, :current_user
def connect
# initializes Warden after a cold start, in case you're visitor #1
env["warden"].authenticated?
# the problem with only using session is that users often login on multiple devices
self.current_user = env["warden"].user
# the problem with only using user is that sometimes you might want to use SR before you login
self.session_id = request.session.id
# and so, we recommend using both
# this assumes that you want to enable SR for unauthenticated users
reject_unauthorized_connection unless current_user || session_id
# if you want to disable SR for unauthenticated users,
# comment out the line above and uncomment the line below
# reject_unauthorized_connection unless current_user
end
end
end

View File

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

View File

@@ -1,8 +0,0 @@
%div{ id: @id, "data-controller": "modal #{@controllers}", "data-action": "keyup@document->modal#closeIfEscapeKey" }
.reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" }
.reveal-modal.fade.tiny.help-modal{ "data-modal-target": "modal" }
= content
.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 }

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

@@ -1,9 +0,0 @@
# frozen_string_literal: true
class MultipleCheckedSelectComponent < ViewComponent::Base
def initialize(name:, options:, selected:)
@name = name
@options = options.map { |option| [option[0], option[1].to_sym] }
@selected = selected.nil? ? [] : selected.map(&:to_sym)
end
end

View File

@@ -1,13 +0,0 @@
.ofn-drop-down.ofn-drop-down-v2{ data: { controller: "multiple-checked-select" } }
%div.ofn-drop-down-label{ "data-multiple-checked-select-target": "button" }
%span{class: "label"}= t('admin.columns')
%span{ class: "icon-caret-down", "data-multiple-checked-select-target": "caret" }
%div.menu{ class: "hidden", "data-multiple-checked-select-target": "options" }
%div.filter
%input{ type: "text", "data-multiple-checked-select-target": "filter", placeholder: I18n.t('components.multiple_checked_select.filter_placeholder') }
%hr
%div.menu_items
- @options.each do |option|
%label.menu_item{ "data-multiple-checked-select-target": "option", "data-value": option[1], "data-label": option[0] }
%input.redesigned-input{ type: "checkbox", checked: @selected.include?(option[1]), name: "#{@name}[]", value: option[1] }
= option[0]

View File

@@ -1,15 +0,0 @@
# frozen_string_literal: true
class PaginationComponent < ViewComponentReflex::Component
def initialize(pagy:, data:)
super
@count = pagy.count
@page = pagy.page
@per_page = pagy.items
@pages = pagy.pages
@next = pagy.next
@prev = pagy.prev
@data = data
@series = pagy.series
end
end

View File

@@ -1,16 +0,0 @@
= component_controller do
%nav{"aria-label": "pagination"}
.pagination
.pagination-prev{data: @prev.nil? ? nil : @data, "data-page": @prev, class: "#{'inactive' if @prev.nil?}"}
= I18n.t "components.pagination.previous"
.pagination-pages
- @series.each do |page|
- if page == :gap
.pagination-gap
- else
.pagination-page{data: @data, "data-page": page, class: "#{'active' if page.to_i == @page}"}
= page
.pagination-next{data: @next.nil? ? nil : @data, "data-page": @next, class: "#{'inactive' if @next.nil?}"}
= I18n.t "components.pagination.next"

View File

@@ -1,69 +0,0 @@
nav {
.pagination {
display: flex;
justify-content: space-between;
align-items: flex-start;
font-size: 14px;
.pagination-prev, .pagination-next {
cursor: pointer;
&:after, &:before {
font-size: 2em;
position: relative;
top: 3px;
}
&.inactive {
cursor: default;
color: $disabled-dark;
}
}
.pagination-prev {
margin-left: 10px;
&:before {
content: "";
margin-left: 10px;
margin-right: 10px;
}
}
.pagination-next {
margin-right: 10px;
&:after {
content: "";
margin-left: 10px;
margin-right: 10px;
}
}
.pagination-pages {
display: flex;
align-items: flex-end;
.pagination-gap, .pagination-page {
padding: 0 0.5rem;
margin-left: 10px;
margin-right: 10px;
}
.pagination-gap {
color: $disabled-dark;
}
.pagination-page {
color: $color-4;
cursor: pointer;
&.active {
border-top: 3px solid $spree-blue;
color: $spree-blue;
cursor: default;
}
}
}
}
}

View File

@@ -1,30 +0,0 @@
# frozen_string_literal: true
class ProductComponent < ViewComponentReflex::Component
def initialize(product:, columns:)
super
@product = product
@image = @product.images[0] if product.images.any?
@columns = columns.map { |c|
{
id: c[:value],
value: column_value(c[:value])
}
}
end
def column_value(column)
case column
when 'name'
@product.name
when 'price'
@product.price
when 'unit'
"#{@product.unit_value} #{@product.variant_unit}"
when 'producer'
@product.supplier.name
when 'category'
@product.taxons.map(&:name).join(', ')
end
end
end

View File

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

View File

@@ -1,152 +0,0 @@
# frozen_string_literal: true
class ProductsTableComponent < ViewComponentReflex::Component
include Pagy::Backend
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]
}.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
def initialize(user:)
super
@user = user
@selectable_columns = SELECTABLE_COMUMNS
@columns_selected = ["price", "unit"]
@per_page = PER_PAGE
@per_page_selected = [10]
@categories = [{ label: "All", value: "all" }] +
Spree::Taxon.order(:name)
.map { |taxon| { label: taxon.name, value: taxon.id.to_s } }
@categories_selected = ["all"]
@producers = [{ label: "All", value: "all" }] +
OpenFoodNetwork::Permissions.new(@user)
.managed_product_enterprises.is_primary_producer.by_name
.map { |producer| { label: producer.name, value: producer.id.to_s } }
@producers_selected = ["all"]
@page = 1
@sort = { column: "name", direction: "asc" }
@search_term = ""
end
def before_render
fetch_products
refresh_columns
end
def search_term
@search_term = element.dataset['value']
end
def toggle_column
column = element.dataset['value']
@columns_selected = if @columns_selected.include?(column)
@columns_selected - [column]
else
@columns_selected + [column]
end
end
def click_sort
@sort = { column: element.dataset['sort-value'],
direction: element.dataset['sort-direction'] == "asc" ? "desc" : "asc" }
end
def toggle_per_page
selected = element.dataset['value'].to_i
@per_page_selected = [selected] if PER_PAGE_VALUE.include?(selected)
end
def toggle_category
category_clicked = element.dataset['value']
@categories_selected = toggle_selector_with_filter(category_clicked, @categories_selected)
end
def toggle_producer
producer_clicked = element.dataset['value']
@producers_selected = toggle_selector_with_filter(producer_clicked, @producers_selected)
end
def change_page
page = element.dataset['page'].to_i
@page = page if page > 0
end
private
def refresh_columns
@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
def toggle_selector_with_filter(clicked, selected)
selected = if selected.include?(clicked)
selected - [clicked]
else
selected + [clicked]
end
if clicked == "all" || selected.empty?
selected = ["all"]
elsif selected.include?("all") && selected.length > 1
selected -= ["all"]
end
selected
end
def fetch_products
product_query = OpenFoodNetwork::Permissions.new(@user).editable_products.merge(product_scope)
@products = product_query.ransack(ransack_query).result
@pagy, @products = pagy(@products, items: @per_page_selected.first, page: @page)
end
def product_scope
scope = if @user.has_spree_role?("admin") || @user.enterprises.present?
Spree::Product
else
Spree::Product.active
end
scope.includes(product_query_includes)
end
def ransack_query
query = { s: "#{@sort[:column]} #{@sort[:direction]}" }
query = if @producers_selected.include?("all")
query.merge({ supplier_id_eq: "" })
else
query.merge({ supplier_id_in: @producers_selected })
end
query = query.merge({ name_cont: @search_term }) if @search_term.present?
if @categories_selected.include?("all")
query.merge({ primary_taxon_id_eq: "" })
else
query.merge({ primary_taxon_id_in: @categories_selected })
end
end
def product_query_includes
[
master: [:images],
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides,
{ option_values: :option_type }]
]
end
end

View File

@@ -1,21 +0,0 @@
= component_controller(class: "products-table") do
.products-table-form
.products-table-form_filter_results
= render(SearchInputComponent.new(value: @search_term, data: reflex_data_attributes(:search_term)))
.products-table-form_categories_selector
= render(SelectorWithFilterComponent.new(title: t("admin.products_page.filters.categories.title"), selected: @categories_selected, items: @categories, data: reflex_data_attributes(:toggle_category), selected_items_i18n_key: "admin.products_page.filters.categories.selected_categories"))
.products-table-form_producers_selector
= render(SelectorWithFilterComponent.new(title: t("admin.products_page.filters.producers.title"), selected: @producers_selected, items: @producers, data: reflex_data_attributes(:toggle_producer), selected_items_i18n_key: "admin.products_page.filters.producers.selected_producers"))
.products-table-form_per-page_selector
= render(SelectorComponent.new(title: t('admin.products_page.filters.per_page', count: @per_page_selected[0]), selected: @per_page_selected, items: @per_page, data: reflex_data_attributes(:toggle_per_page)))
.products-table-form_columns_selector
= render(SelectorComponent.new(title: t("admin.products_page.filters.columns"), selected: @columns_selected, items: @selectable_columns, data: reflex_data_attributes(:toggle_column)))
.products-table_table
%table
= render(TableHeaderComponent.new(columns: @columns, sort: @sort, data: reflex_data_attributes(:click_sort)))
%tbody
= render(ProductComponent.with_collection(@products, columns: @columns))
.products-table-form_pagination
= render(PaginationComponent.new(pagy: @pagy, data: reflex_data_attributes(:change_page)))

View File

@@ -1,47 +0,0 @@
.products-table {
.products-table-form {
display: grid;
grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );
grid-gap: 10px;
margin-bottom: 10px;
}
.products-table_table {
box-shadow: 0 10px 10px -1px rgb(0 0 0 / 10%);
}
.products-table-form_pagination {
position: relative;
top: -15px;
nav, .pagination {
margin-top: 0;
padding-top: 0;
}
}
}
.products-table.loading {
.products-table-form_pagination, .products-table_table {
position: relative;
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.5);
}
}
.products-table_table {
&:before {
background-position: center;
background-repeat: no-repeat;
background-size: 50px 50px;
background-image: url("../images/spinning-circles.svg");
}
}
}

View File

@@ -1,9 +0,0 @@
# frozen_string_literal: true
class SearchInputComponent < ViewComponentReflex::Component
def initialize(value: nil, data: {})
super
@value = value
@data = data
end
end

View File

@@ -1,5 +0,0 @@
= component_controller do
%div.search-input
%input{type: 'text', placeholder: t("components.search_input.placeholder"), id: 'search_query', data: {action: 'debounced:input->search-input#search'}, value: @value}
.search-button{data: @data}
%i.fa.fa-search

View File

@@ -1,23 +0,0 @@
.search-input {
border: 1px solid $disabled-light;
height: 3em;
display: flex;
line-height: 3em;
align-items: center;
input {
border: none;
height: 3em;
width: 100%;
box-sizing: border-box;
padding-right: 5px;
}
.search-button {
padding-right: 10px;
padding-left: 5px;
cursor: pointer;
color: $color-4;
}
}

View File

@@ -1,17 +0,0 @@
# frozen_string_literal: true
class SelectorComponent < ViewComponentReflex::Component
def initialize(title:, selected:, items:, data: {})
super
@title = title
@items = items.map do |item|
{
label: item[:label],
value: item[:value],
selected: selected.include?(item[:value])
}
end
@selected = selected
@data = data
end
end

View File

@@ -1,11 +0,0 @@
= component_controller do
.selector.selector-close
.selector-main{ data: { action: "click->selector#toggle" } }
.selector-main-title
= @title
.selector-arrow
.selector-wrapper
.selector-items
- @items.each do |item|
.selector-item{ class: ("selected" if item[:selected]), data: @data, "data-value": item[:value] }
= item[:label]

View File

@@ -1,86 +0,0 @@
.selector {
position: relative;
.selector-main {
border: 1px solid $disabled-light;
height: 3em;
position: relative;
cursor: pointer;
.selector-main-title {
line-height: 3em;
padding-left: 10px;
padding-right: 10px;
}
.selector-arrow {
position: absolute;
right: 0px;
height: 3em;
width: 1.5em;
top: -1px;
&:after {
content: "";
position: absolute;
top: 50%;
right: 5px;
margin-top: -5px;
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid $disabled-light;
}
}
}
.selector-wrapper {
position: absolute;
left: 0px;
right: 0px;
z-index: 1;
background-color: white;
margin-top: -1px;
border: 1px solid $disabled-light;
.selector-items {
overflow-y: auto;
min-height: 6em;
.selector-item {
padding-left: 10px;
padding-right: 10px;
border-bottom: 1px solid $disabled-light;
position: relative;
height: 3em;
line-height: 3em;
&:hover {
background-color: #eee;
cursor: pointer;
}
&:last-child {
border-bottom: none;
}
&.selected {
&:after {
content: "";
display: inline-block;
position: absolute;
right: 10px;
}
}
}
}
}
&.selector-close {
.selector-wrapper {
display: none;
}
}
}

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