mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-14 18:56:49 +00:00
Compare commits
288 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8ec71d0d8 | ||
|
|
006ad6be73 | ||
|
|
d995e7630c | ||
|
|
36b3cbb300 | ||
|
|
0a5eb9386c | ||
|
|
d801b6e970 | ||
|
|
7fe7a672ae | ||
|
|
fd95970774 | ||
|
|
a046d0538b | ||
|
|
9cd35ddd65 | ||
|
|
ac87daab4b | ||
|
|
bd0b251724 | ||
|
|
f3d82b41ad | ||
|
|
a1d939af24 | ||
|
|
eeacdb676e | ||
|
|
21366f8109 | ||
|
|
e4bfe7fac6 | ||
|
|
a8278446b3 | ||
|
|
10bfe00123 | ||
|
|
e790fc2b7b | ||
|
|
deff728ca8 | ||
|
|
064e97d7ee | ||
|
|
3003ba0fc0 | ||
|
|
9fbdb311b0 | ||
|
|
5ed6e5599d | ||
|
|
900ff6d053 | ||
|
|
4d605c7060 | ||
|
|
ffba628691 | ||
|
|
8b416a8eda | ||
|
|
2c18e97fb8 | ||
|
|
e11d644a95 | ||
|
|
26131d105e | ||
|
|
cfa0a502f1 | ||
|
|
869a6b42d3 | ||
|
|
fdf3a0d7b7 | ||
|
|
d5c9c53d8b | ||
|
|
1f98ef5074 | ||
|
|
996d3afe4f | ||
|
|
500c4e8a2f | ||
|
|
5002870b39 | ||
|
|
d97e9ae824 | ||
|
|
f0e0bac3b5 | ||
|
|
ec31d63c58 | ||
|
|
9dfc22451e | ||
|
|
3cf01623da | ||
|
|
274ee2c6f6 | ||
|
|
940b554d23 | ||
|
|
91d1ecea2e | ||
|
|
fbf2315a93 | ||
|
|
428256a323 | ||
|
|
20218c00ab | ||
|
|
18adcb7fa4 | ||
|
|
56b94342eb | ||
|
|
758c40c646 | ||
|
|
98391b60c6 | ||
|
|
3e03208cf5 | ||
|
|
3ae5db907a | ||
|
|
27c1fe2f06 | ||
|
|
122677bc7a | ||
|
|
22d13621f4 | ||
|
|
265b4823a7 | ||
|
|
5e689fcce3 | ||
|
|
e801bb5e40 | ||
|
|
d75c62a621 | ||
|
|
1de7fb6fe8 | ||
|
|
d37cd09c84 | ||
|
|
cb24efbc4a | ||
|
|
c23d4f63ed | ||
|
|
c7197364d1 | ||
|
|
069b314ae7 | ||
|
|
7716d2734e | ||
|
|
8adcdf14a7 | ||
|
|
224daf2591 | ||
|
|
4fa88b9c18 | ||
|
|
1adb22be71 | ||
|
|
d4cfa7b368 | ||
|
|
5c5a0c98f1 | ||
|
|
8ce3c9f449 | ||
|
|
5ea7bea9b8 | ||
|
|
461d31bef1 | ||
|
|
8758a2701c | ||
|
|
2b7bccf890 | ||
|
|
7692cebbd3 | ||
|
|
1869536529 | ||
|
|
d7d29e3654 | ||
|
|
2aea5d4957 | ||
|
|
a328888fc2 | ||
|
|
c33c4860c8 | ||
|
|
efe8030786 | ||
|
|
76412bfcae | ||
|
|
69faf590b0 | ||
|
|
2f037c3965 | ||
|
|
70193539b2 | ||
|
|
8ee5050a49 | ||
|
|
2e4a8f8ae8 | ||
|
|
8ee05789ef | ||
|
|
e2a8ab16b4 | ||
|
|
6fb337b81c | ||
|
|
387a46e1db | ||
|
|
1db85e295b | ||
|
|
7df2ad13c6 | ||
|
|
fee99a95f8 | ||
|
|
ab70a85866 | ||
|
|
d31c2d95b3 | ||
|
|
44af982b3e | ||
|
|
2ea22248e0 | ||
|
|
9d58f16012 | ||
|
|
f2838105fe | ||
|
|
500c23ce27 | ||
|
|
fac8926396 | ||
|
|
bd4b3110c1 | ||
|
|
f7cd168f6d | ||
|
|
07c29df801 | ||
|
|
6ac21eaab4 | ||
|
|
ff0b84c9c0 | ||
|
|
26f40dea6f | ||
|
|
188a0f5d25 | ||
|
|
454c3d6e31 | ||
|
|
f76f885ffa | ||
|
|
f654e52fbb | ||
|
|
b8400e3655 | ||
|
|
8168f122a0 | ||
|
|
7a2084e4e7 | ||
|
|
92dcf926b5 | ||
|
|
1fcc98af80 | ||
|
|
67dbf552b1 | ||
|
|
899879b88d | ||
|
|
9a57935709 | ||
|
|
5de928f452 | ||
|
|
2fa05ae248 | ||
|
|
ca795fcf6e | ||
|
|
524d68496a | ||
|
|
b117608229 | ||
|
|
1ccd580c7b | ||
|
|
bff38b2c5b | ||
|
|
eb911d9fe3 | ||
|
|
9fa46d378f | ||
|
|
2e5f0d302f | ||
|
|
3c4845813d | ||
|
|
7166fa6a25 | ||
|
|
9d36d1942e | ||
|
|
9dc3f2d69e | ||
|
|
7b07817bbf | ||
|
|
a36627927d | ||
|
|
cc80949d56 | ||
|
|
5c8bbb3a38 | ||
|
|
e8669f0178 | ||
|
|
ef31e1d063 | ||
|
|
4ef5609b0e | ||
|
|
4e70a86fa9 | ||
|
|
44f52c21db | ||
|
|
e8b8a8961e | ||
|
|
9a849bb2ee | ||
|
|
9c8525b797 | ||
|
|
a9edc0cb9c | ||
|
|
7556eee5d4 | ||
|
|
44760ce8b6 | ||
|
|
005d5cafde | ||
|
|
25ab334d73 | ||
|
|
85dc32ffcb | ||
|
|
763d922510 | ||
|
|
a8bb05c575 | ||
|
|
0ce1ccf2d6 | ||
|
|
e288c7a551 | ||
|
|
dc030182db | ||
|
|
ddd0b939fe | ||
|
|
a4b6c05d58 | ||
|
|
b3d0c0f2d5 | ||
|
|
3afd8b7189 | ||
|
|
7d51bd7b83 | ||
|
|
974842300c | ||
|
|
9474b574ad | ||
|
|
3a490a1f26 | ||
|
|
fce3c46e55 | ||
|
|
a95bef141f | ||
|
|
e20e519201 | ||
|
|
c35c003e5a | ||
|
|
e25f4b1daa | ||
|
|
7b965718aa | ||
|
|
61e7b59437 | ||
|
|
ff9544e08b | ||
|
|
029c0afaa9 | ||
|
|
33ee03388f | ||
|
|
4d49266f0f | ||
|
|
aea6f864d9 | ||
|
|
4cdb224434 | ||
|
|
8d6615aa6b | ||
|
|
b0aa7b7b55 | ||
|
|
c90d2c7f9a | ||
|
|
63c1cd7bff | ||
|
|
ee3ec15bc4 | ||
|
|
b9a43df7fe | ||
|
|
fd274447fe | ||
|
|
52a98989e0 | ||
|
|
96193a27a4 | ||
|
|
2630fde763 | ||
|
|
a501bc9687 | ||
|
|
f25fb19fe8 | ||
|
|
25ed405da7 | ||
|
|
00176b2b95 | ||
|
|
8f25718e62 | ||
|
|
98b9395c31 | ||
|
|
540c673924 | ||
|
|
4149c14e8d | ||
|
|
af43fe9a5b | ||
|
|
2a3c4d6649 | ||
|
|
c954187322 | ||
|
|
dbb7155114 | ||
|
|
27b396cbc2 | ||
|
|
1abc406156 | ||
|
|
5cab0f7f3d | ||
|
|
56ce4ab04f | ||
|
|
d351bf9a9a | ||
|
|
b447e4052c | ||
|
|
29196375f5 | ||
|
|
23570be6a1 | ||
|
|
845133d3f6 | ||
|
|
b6a7ca3047 | ||
|
|
6652b7fe5c | ||
|
|
6cb17905cd | ||
|
|
6aa449aacb | ||
|
|
c2f82202a6 | ||
|
|
6c219b31a3 | ||
|
|
b889f31c1a | ||
|
|
83c7764456 | ||
|
|
23c3573b8f | ||
|
|
7c8a3bd7c9 | ||
|
|
b66d263bc6 | ||
|
|
9afa75d91c | ||
|
|
d15708d4e3 | ||
|
|
dafbefe8e3 | ||
|
|
9d33402d9e | ||
|
|
abf8d44935 | ||
|
|
fb15b19e88 | ||
|
|
e5c6ff433c | ||
|
|
bb35629960 | ||
|
|
c4e75dc557 | ||
|
|
10d1d2066e | ||
|
|
3c53214daa | ||
|
|
40a9c36c85 | ||
|
|
48ecbaaa16 | ||
|
|
ef7684e079 | ||
|
|
04c3b8b9cf | ||
|
|
fd3a54a904 | ||
|
|
236abe485a | ||
|
|
8fb11b9151 | ||
|
|
b9471d4f01 | ||
|
|
b5da120805 | ||
|
|
da5976d85d | ||
|
|
9e1047288f | ||
|
|
ff9985dd88 | ||
|
|
e62eca93c1 | ||
|
|
8d532ae262 | ||
|
|
d2f28e2095 | ||
|
|
dbd5186dfa | ||
|
|
61cb7c4402 | ||
|
|
ccfb83d2cd | ||
|
|
d6aa0168b2 | ||
|
|
2a8ef5a45e | ||
|
|
c423641102 | ||
|
|
22559a755f | ||
|
|
e877f94e07 | ||
|
|
820fff0528 | ||
|
|
09bcc772ff | ||
|
|
49e376f9b6 | ||
|
|
85d3f5f9d7 | ||
|
|
9dd81604e9 | ||
|
|
61d3f79444 | ||
|
|
b33d382069 | ||
|
|
a8816b3788 | ||
|
|
204295dc91 | ||
|
|
4ed71f1883 | ||
|
|
92585b65c5 | ||
|
|
099f75befb | ||
|
|
2eaf79b149 | ||
|
|
60f0a58508 | ||
|
|
688020a304 | ||
|
|
4b1bd362e0 | ||
|
|
4b3465de78 | ||
|
|
b358468952 | ||
|
|
c1c0a72184 | ||
|
|
fbe6dcba7a | ||
|
|
4f265eed2a | ||
|
|
bdf132a833 | ||
|
|
262b546836 | ||
|
|
4bfd39caec | ||
|
|
58c8eae08c | ||
|
|
cb1b24695e |
412
.github/workflows/build.yml
vendored
412
.github/workflows/build.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
DISABLE_KNAPSACK: true
|
||||
DISABLE_KNAPSACK_PRO: false
|
||||
TIMEZONE: UTC
|
||||
COVERAGE: true
|
||||
RAILS_ENV: test
|
||||
@@ -17,7 +17,7 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
rspec:
|
||||
knapsack_rspec_controllers:
|
||||
runs-on: ubuntu-20.04
|
||||
services:
|
||||
postgres:
|
||||
@@ -33,19 +33,15 @@ jobs:
|
||||
POSTGRES_USER: ofn
|
||||
POSTGRES_PASSWORD: f00d
|
||||
strategy:
|
||||
matrix:
|
||||
specs:
|
||||
- "spec/controllers"
|
||||
- "spec/models"
|
||||
- "spec/lib"
|
||||
- "spec/migrations"
|
||||
- "spec/serializers"
|
||||
- "spec/system/admin/[a-o0-9]*"
|
||||
- "spec/system/admin/[p-z]*"
|
||||
- "spec/system/consumer/[a-o0-9]*"
|
||||
- "spec/system/consumer/[p-z]*"
|
||||
- "engines/*/spec"
|
||||
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]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -72,7 +68,161 @@ jobs:
|
||||
bundle exec rake db:schema:load
|
||||
|
||||
- name: Run tests
|
||||
run: bundle exec rspec --profile -- ${{ matrix.specs }}
|
||||
|
||||
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
|
||||
|
||||
- name: Archive failed tests screenshots
|
||||
if: failure()
|
||||
@@ -83,7 +233,233 @@ jobs:
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
test-the-rest:
|
||||
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
|
||||
services:
|
||||
postgres:
|
||||
@@ -122,12 +498,8 @@ 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 rspec --exclude-pattern "./models/**/*_spec.rb, ./controllers/**/*_spec.rb, ./serializers/**/*_spec.rb, ./lib/**/*_spec.rb, ./migrations/**/*_spec.rb, ./system/**/*_spec.rb"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# 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
|
||||
|
||||
@@ -440,6 +440,7 @@ Metrics/BlockNesting:
|
||||
# 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'
|
||||
@@ -681,7 +682,7 @@ Rails/ActiveRecordOverride:
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Rails/ApplicationController:
|
||||
Exclude:
|
||||
- 'engines/dfc_provider/app/controllers/dfc_provider/api/base_controller.rb'
|
||||
- 'engines/dfc_provider/app/controllers/dfc_provider/base_controller.rb'
|
||||
|
||||
# Offense count: 6
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
|
||||
6
Gemfile
6
Gemfile
@@ -135,6 +135,9 @@ gem 'flipper-active_record'
|
||||
gem 'flipper-ui'
|
||||
|
||||
gem "view_component"
|
||||
gem 'view_component_reflex', '3.1.14.pre9'
|
||||
|
||||
gem 'mini_portile2', '~> 2.8'
|
||||
|
||||
group :production, :staging do
|
||||
gem 'ddtrace'
|
||||
@@ -150,7 +153,7 @@ 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', require: false
|
||||
gem 'knapsack_pro'
|
||||
gem 'letter_opener', '>= 1.4.1'
|
||||
gem 'rspec-rails', ">= 3.5.2"
|
||||
gem 'rspec-retry', require: false
|
||||
@@ -164,7 +167,6 @@ group :test do
|
||||
gem 'pdf-reader'
|
||||
gem 'rails-controller-testing'
|
||||
gem 'simplecov', require: false
|
||||
gem 'test-prof', require: false
|
||||
gem 'vcr', require: false
|
||||
gem 'webmock', require: false
|
||||
# See spec/spec_helper.rb for instructions
|
||||
|
||||
76
Gemfile.lock
76
Gemfile.lock
@@ -158,16 +158,16 @@ GEM
|
||||
awesome_nested_set (3.5.0)
|
||||
activerecord (>= 4.0.0, < 7.1)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.651.0)
|
||||
aws-sdk-core (3.166.0)
|
||||
aws-partitions (1.669.0)
|
||||
aws-sdk-core (3.168.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.59.0)
|
||||
aws-sdk-kms (1.60.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.117.1)
|
||||
aws-sdk-s3 (1.117.2)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
@@ -177,12 +177,12 @@ GEM
|
||||
bigdecimal (3.0.2)
|
||||
bindata (2.4.12)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.13.0)
|
||||
bootsnap (1.15.0)
|
||||
msgpack (~> 1.2)
|
||||
bugsnag (6.24.2)
|
||||
bugsnag (6.25.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
builder (3.2.4)
|
||||
bullet (7.0.3)
|
||||
bullet (7.0.4)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
cable_ready (5.0.0.pre9)
|
||||
@@ -241,8 +241,8 @@ GEM
|
||||
debase-ruby_core_source (= 0.10.12)
|
||||
msgpack
|
||||
debase-ruby_core_source (0.10.12)
|
||||
debug (1.6.3)
|
||||
irb (>= 1.3.6)
|
||||
debug (1.7.0)
|
||||
irb (>= 1.5.0)
|
||||
reline (>= 0.3.1)
|
||||
debugger-linecache (1.2.0)
|
||||
devise (4.8.1)
|
||||
@@ -258,7 +258,7 @@ GEM
|
||||
devise-token_authenticatable (1.1.0)
|
||||
devise (>= 4.0.0, < 5.0.0)
|
||||
diff-lcs (1.5.0)
|
||||
digest (3.1.0)
|
||||
digest (3.1.1)
|
||||
docile (1.4.0)
|
||||
dotenv (2.8.1)
|
||||
dotenv-rails (2.8.1)
|
||||
@@ -351,16 +351,16 @@ GEM
|
||||
activerecord (>= 3.0)
|
||||
io-console (0.5.11)
|
||||
ipaddress (0.8.3)
|
||||
irb (1.4.2)
|
||||
irb (1.5.1)
|
||||
reline (>= 0.3.0)
|
||||
jmespath (1.6.1)
|
||||
jmespath (1.6.2)
|
||||
jquery-rails (4.4.0)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (4.2.1)
|
||||
railties (>= 3.2.16)
|
||||
json (2.6.2)
|
||||
json (2.6.3)
|
||||
json-jwt (1.16.0)
|
||||
activesupport (>= 4.2)
|
||||
aes_key_wrap
|
||||
@@ -375,7 +375,7 @@ GEM
|
||||
jsonapi-serializer (2.2.0)
|
||||
activesupport (>= 4.2)
|
||||
jwt (2.5.0)
|
||||
knapsack (4.0.0)
|
||||
knapsack_pro (3.6.0)
|
||||
rake
|
||||
launchy (2.5.0)
|
||||
addressable (~> 2.7)
|
||||
@@ -385,7 +385,7 @@ GEM
|
||||
listen (3.7.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
loofah (2.19.0)
|
||||
loofah (2.19.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
@@ -409,7 +409,7 @@ GEM
|
||||
money (~> 6.12)
|
||||
money (6.16.0)
|
||||
i18n (>= 0.6.4, <= 2)
|
||||
msgpack (1.5.4)
|
||||
msgpack (1.6.0)
|
||||
multi_json (1.15.0)
|
||||
multi_xml (0.6.0)
|
||||
net-protocol (0.1.3)
|
||||
@@ -417,7 +417,7 @@ GEM
|
||||
net-smtp (0.3.2)
|
||||
net-protocol
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.13.9)
|
||||
nokogiri (1.13.10)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
oauth2 (1.4.11)
|
||||
@@ -453,7 +453,7 @@ GEM
|
||||
parallel (1.22.1)
|
||||
paranoia (2.6.1)
|
||||
activerecord (>= 5.1, < 7.1)
|
||||
parser (3.1.2.1)
|
||||
parser (3.1.3.0)
|
||||
ast (~> 2.4.1)
|
||||
paypal-sdk-core (0.3.4)
|
||||
multi_json (~> 1.0)
|
||||
@@ -467,7 +467,7 @@ GEM
|
||||
ruby-rc4
|
||||
ttfunk
|
||||
pg (1.2.3)
|
||||
power_assert (2.0.1)
|
||||
power_assert (2.0.2)
|
||||
pry (0.13.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
@@ -475,7 +475,7 @@ GEM
|
||||
puma (5.6.5)
|
||||
nio4r (~> 2.0)
|
||||
raabro (1.4.0)
|
||||
racc (1.6.0)
|
||||
racc (1.6.1)
|
||||
rack (2.2.4)
|
||||
rack-mini-profiler (2.3.4)
|
||||
rack (>= 1.2.0)
|
||||
@@ -522,8 +522,8 @@ GEM
|
||||
activesupport (>= 4.2)
|
||||
choice (~> 0.2.0)
|
||||
ruby-graphviz (~> 1.2)
|
||||
rails-html-sanitizer (1.4.3)
|
||||
loofah (~> 2.3)
|
||||
rails-html-sanitizer (1.4.4)
|
||||
loofah (~> 2.19, >= 2.19.1)
|
||||
rails-i18n (7.0.6)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
@@ -545,7 +545,7 @@ GEM
|
||||
ffi (~> 1.0)
|
||||
redcarpet (3.5.1)
|
||||
redis (4.8.0)
|
||||
regexp_parser (2.6.0)
|
||||
regexp_parser (2.6.1)
|
||||
reline (0.3.1)
|
||||
io-console (~> 0.5)
|
||||
request_store (1.5.0)
|
||||
@@ -589,17 +589,17 @@ GEM
|
||||
rspec-retry (0.6.2)
|
||||
rspec-core (> 3.3)
|
||||
rspec-support (3.10.3)
|
||||
rswag-api (2.7.0)
|
||||
rswag-api (2.8.0)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rswag-specs (2.8.0)
|
||||
activesupport (>= 3.1, < 7.1)
|
||||
json-schema (>= 2.2, < 4.0)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rspec-core (>= 2.14)
|
||||
rswag-ui (2.7.0)
|
||||
rswag-ui (2.8.0)
|
||||
actionpack (>= 3.1, < 7.1)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rubocop (1.39.0)
|
||||
rubocop (1.40.0)
|
||||
json (~> 2.3)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.1.2.1)
|
||||
@@ -609,9 +609,9 @@ GEM
|
||||
rubocop-ast (>= 1.23.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 3.0)
|
||||
rubocop-ast (1.23.0)
|
||||
rubocop-ast (1.24.0)
|
||||
parser (>= 3.1.1.0)
|
||||
rubocop-rails (2.17.2)
|
||||
rubocop-rails (2.17.3)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
@@ -682,19 +682,18 @@ GEM
|
||||
railties (>= 5.2)
|
||||
redis
|
||||
stringex (2.8.5)
|
||||
stripe (7.1.0)
|
||||
stripe (8.0.0)
|
||||
swd (1.3.0)
|
||||
activesupport (>= 3)
|
||||
attr_required (>= 0.0.5)
|
||||
httpclient (>= 2.4)
|
||||
temple (0.8.2)
|
||||
test-prof (1.0.11)
|
||||
test-unit (3.5.5)
|
||||
test-unit (3.5.7)
|
||||
power_assert
|
||||
thor (1.2.1)
|
||||
thread-local (1.1.0)
|
||||
tilt (2.0.11)
|
||||
timecop (0.9.5)
|
||||
timecop (0.9.6)
|
||||
timeout (0.3.0)
|
||||
ttfunk (1.7.0)
|
||||
tzinfo (2.0.5)
|
||||
@@ -711,10 +710,14 @@ GEM
|
||||
activemodel (>= 3.0.0)
|
||||
public_suffix
|
||||
vcr (6.1.0)
|
||||
view_component (2.76.0)
|
||||
view_component (2.79.0)
|
||||
activesupport (>= 5.0.0, < 8.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
method_source (~> 1.0)
|
||||
view_component_reflex (3.1.14.pre9)
|
||||
rails (>= 5.2, < 8.0)
|
||||
stimulus_reflex (>= 3.5.0.pre2)
|
||||
view_component (>= 2.28.0)
|
||||
view_component_storybook (0.11.1)
|
||||
view_component (>= 2.36)
|
||||
warden (1.2.9)
|
||||
@@ -744,7 +747,7 @@ GEM
|
||||
chronic (>= 0.6.3)
|
||||
wicked_pdf (2.6.3)
|
||||
activesupport
|
||||
wkhtmltopdf-binary (0.12.6.5)
|
||||
wkhtmltopdf-binary (0.12.6.6)
|
||||
xml-simple (1.1.8)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
@@ -818,11 +821,12 @@ DEPENDENCIES
|
||||
json_spec (~> 1.1.4)
|
||||
jsonapi-serializer
|
||||
jwt (~> 2.3)
|
||||
knapsack
|
||||
knapsack_pro
|
||||
letter_opener (>= 1.4.1)
|
||||
listen
|
||||
mime-types
|
||||
mimemagic (> 0.3.5)
|
||||
mini_portile2 (~> 2.8)
|
||||
mini_racer (= 0.4.0)
|
||||
monetize (~> 1.11)
|
||||
oauth2 (~> 1.4.7)
|
||||
@@ -874,12 +878,12 @@ DEPENDENCIES
|
||||
stimulus_reflex (= 3.5.0.pre9)
|
||||
stringex (~> 2.8.5)
|
||||
stripe
|
||||
test-prof
|
||||
test-unit (~> 3.5)
|
||||
timecop
|
||||
valid_email2
|
||||
vcr
|
||||
view_component
|
||||
view_component_reflex (= 3.1.14.pre9)
|
||||
view_component_storybook
|
||||
web!
|
||||
web-console
|
||||
|
||||
3
Rakefile
3
Rakefile
@@ -8,6 +8,3 @@ require_relative 'config/application'
|
||||
|
||||
Openfoodnetwork::Application.load_tasks
|
||||
|
||||
if !ENV['DISABLE_KNAPSACK'] && defined?(Knapsack)
|
||||
Knapsack.load_tasks
|
||||
end
|
||||
|
||||
@@ -38,3 +38,4 @@ angular.module('admin.orderCycles')
|
||||
$scope.removeCoordinatorFee = ($event, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeCoordinatorFee(index)
|
||||
$scope.order_cycle_form.$dirty = true
|
||||
|
||||
15
app/components/pagination_component.rb
Normal file
15
app/components/pagination_component.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# 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
|
||||
@@ -0,0 +1,16 @@
|
||||
= 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"
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
app/components/product_component.rb
Normal file
30
app/components/product_component.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
# 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
|
||||
@@ -0,0 +1,6 @@
|
||||
%tr
|
||||
- @columns.each do |column|
|
||||
%td.products_column{class: column[:id]}
|
||||
- if column[:id] == "name" && @image
|
||||
= image_tag @image.url(:mini)
|
||||
= column[:value]
|
||||
152
app/components/products_table_component.rb
Normal file
152
app/components/products_table_component.rb
Normal file
@@ -0,0 +1,152 @@
|
||||
# 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
|
||||
@@ -0,0 +1,21 @@
|
||||
= 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)))
|
||||
@@ -0,0 +1,47 @@
|
||||
.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");
|
||||
}
|
||||
}
|
||||
}
|
||||
9
app/components/search_input_component.rb
Normal file
9
app/components/search_input_component.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SearchInputComponent < ViewComponentReflex::Component
|
||||
def initialize(value: nil, data: {})
|
||||
super
|
||||
@value = value
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
= 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
|
||||
@@ -0,0 +1,23 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
}
|
||||
17
app/components/selector_component.rb
Normal file
17
app/components/selector_component.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# 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
|
||||
@@ -0,0 +1,11 @@
|
||||
= 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]
|
||||
86
app/components/selector_component/selector_component.scss
Normal file
86
app/components/selector_component/selector_component.scss
Normal file
@@ -0,0 +1,86 @@
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
app/components/selector_with_filter_component.rb
Normal file
11
app/components/selector_with_filter_component.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SelectorWithFilterComponent < SelectorComponent
|
||||
def initialize(title:, selected:, items:, data: {},
|
||||
selected_items_i18n_key: 'components.selector_with_filter.selected_items')
|
||||
super(title: title, selected: selected, items: items, data: data)
|
||||
@selected_items = items.select { |item| @selected.include?(item[:value]) }
|
||||
@selected_items_i18n_key = selected_items_i18n_key
|
||||
@items = items
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
= component_controller do
|
||||
.super-selector.selector.selector-close
|
||||
.selector-main{ data: { action: "click->selector-with-filter#toggle" } }
|
||||
.super-selector-label
|
||||
= @title
|
||||
.super-selector-selected-items
|
||||
- case @selected_items.length
|
||||
- when 1, 2
|
||||
- @selected_items.each do |item|
|
||||
.super-selector-selected-item
|
||||
= item[:label]
|
||||
- else
|
||||
.super-selector-selected-item
|
||||
= t(@selected_items_i18n_key, count: @selected_items.length)
|
||||
.selector-arrow
|
||||
.selector-wrapper
|
||||
.super-selector-search
|
||||
%input{type: "text", placeholder: t("components.selector_with_filter.search_placeholder"), data: { action: "debounced:input->selector-with-filter#filter" } }
|
||||
.selector-items
|
||||
- @items.each do |item|
|
||||
.selector-item{ class: ("selected" if item[:selected]), data: @data.merge({ "selector-with-filter-target": "items" }), "data-value": item[:value] }
|
||||
= item[:label]
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
.super-selector {
|
||||
position: relative;
|
||||
|
||||
.selector-main {
|
||||
.super-selector-label {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
margin-left: 10px;
|
||||
position: absolute;
|
||||
top: -1em;
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.super-selector-selected-items {
|
||||
margin-left: 5px;
|
||||
margin-right: 2em;
|
||||
margin-top: 7px;
|
||||
display: flex;
|
||||
|
||||
.super-selector-selected-item {
|
||||
border: 1px solid $pale-blue;
|
||||
background-color: $spree-light-blue;
|
||||
border-radius: 20px;
|
||||
height: 2em;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
padding-top: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.selector-wrapper {
|
||||
.super-selector-search {
|
||||
border-bottom: 1px solid $disabled-light;
|
||||
padding: 10px 5px;
|
||||
|
||||
input {
|
||||
border: 1px solid $disabled-light;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
app/components/table_header_component.rb
Normal file
10
app/components/table_header_component.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TableHeaderComponent < ViewComponentReflex::Component
|
||||
def initialize(columns:, sort:, data: {})
|
||||
super
|
||||
@columns = columns
|
||||
@sort = sort
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
= component_controller do
|
||||
%thead.table-header
|
||||
%tr
|
||||
- @columns.each do |column|
|
||||
%th{class: (column[:sortable] ? "th-sortable " : "" ) + (@sort[:column] == column[:value] ? " th-sorted-#{@sort[:direction]}" : ""), data: (@data if column[:sortable] == true), "data-sort-value": column[:value], "data-sort-direction": @sort[:direction]}
|
||||
= column[:label]
|
||||
@@ -0,0 +1,23 @@
|
||||
thead.table-header {
|
||||
th {
|
||||
&.th-sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
&.th-sorted-asc, &.th-sorted-desc {
|
||||
&:after {
|
||||
display: inline-block;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
&.th-sorted-asc {
|
||||
&:after {
|
||||
content: "⇧";
|
||||
}
|
||||
}
|
||||
&.th-sorted-desc {
|
||||
&:after {
|
||||
content: "⇩";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,21 +116,7 @@ module Admin
|
||||
|
||||
# Fetches tags for all customers of the enterprise and returns a hash indexed by customer_id
|
||||
def customer_tags_by_id
|
||||
customer_tags = ::ActsAsTaggableOn::Tag.
|
||||
joins(:taggings).
|
||||
includes(:taggings).
|
||||
where(taggings:
|
||||
{ taggable_type: 'Customer',
|
||||
taggable_id: Customer.of(managed_enterprise_id),
|
||||
context: 'tags' })
|
||||
|
||||
customer_tags.each_with_object({}) do |tag, indexed_hash|
|
||||
tag.taggings.each do |tagging|
|
||||
customer_id = tagging.taggable_id
|
||||
indexed_hash[customer_id] ||= []
|
||||
indexed_hash[customer_id] << tag.name
|
||||
end
|
||||
end
|
||||
BatchTaggableTagsQuery.call(Customer.of(managed_enterprise_id))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
7
app/controllers/admin/products_controller.rb
Normal file
7
app/controllers/admin/products_controller.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class ProductsController < Spree::Admin::BaseController
|
||||
def index; end
|
||||
end
|
||||
end
|
||||
@@ -48,15 +48,7 @@ module Admin
|
||||
else
|
||||
I18n.t(:name, scope: [:admin, :reports, @report_type])
|
||||
end
|
||||
|
||||
# Initialize data
|
||||
params[:display_summary_row] = true if request.get?
|
||||
@params_fields_to_show = if request.get?
|
||||
@report.columns.keys - @report.fields_to_hide
|
||||
else
|
||||
params[:fields_to_show]
|
||||
end
|
||||
|
||||
@rendering_options = rendering_options
|
||||
@data = Reporting::FrontendData.new(spree_current_user)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -162,25 +162,20 @@ module Admin
|
||||
[:index]
|
||||
end
|
||||
|
||||
def managed_enterprise_id
|
||||
Enterprise.managed_by(spree_current_user).select('enterprises.id').
|
||||
find_by(id: params[:enterprise_id])
|
||||
end
|
||||
|
||||
def subscription_params
|
||||
@subscription_params ||= PermittedAttributes::Subscription.new(params).call.
|
||||
to_h.with_indifferent_access
|
||||
end
|
||||
|
||||
def payment_method_tags_by_id
|
||||
payment_method_tags = ::ActsAsTaggableOn::Tag.
|
||||
joins(:taggings).
|
||||
includes(:taggings).
|
||||
where(taggings: { taggable_type: "Spree::PaymentMethod",
|
||||
taggable_id: Spree::PaymentMethod.from(Enterprise.managed_by(spree_current_user).
|
||||
select('enterprises.id').find_by(id: params[:enterprise_id])),
|
||||
context: 'tags' })
|
||||
|
||||
payment_method_tags.each_with_object({}) do |tag, hash|
|
||||
payment_method_id = tag.taggings.first.taggable_id
|
||||
hash[payment_method_id] ||= []
|
||||
hash[payment_method_id] << tag.name
|
||||
end
|
||||
@payment_method_tags_by_id ||= BatchTaggableTagsQuery.call(
|
||||
Spree::PaymentMethod.from(managed_enterprise_id)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -62,4 +62,37 @@ module ReportsActions
|
||||
def i18n_scope
|
||||
'admin.reports'
|
||||
end
|
||||
|
||||
def rendering_options
|
||||
@rendering_options ||= ReportRenderingOptions.where(
|
||||
user: spree_current_user,
|
||||
report_type: report_type,
|
||||
report_subtype: report_subtype
|
||||
).first_or_create do |report_rendering_options|
|
||||
report_rendering_options.options = {
|
||||
fields_to_show: if request.get?
|
||||
@report.columns.keys -
|
||||
@report.fields_to_hide
|
||||
else
|
||||
params[:fields_to_show]
|
||||
end,
|
||||
display_summary_row: request.get?,
|
||||
display_header_row: false
|
||||
}
|
||||
end
|
||||
update_rendering_options
|
||||
@rendering_options
|
||||
end
|
||||
|
||||
def update_rendering_options
|
||||
return unless request.post?
|
||||
|
||||
@rendering_options.update(
|
||||
options: {
|
||||
fields_to_show: params[:fields_to_show],
|
||||
display_summary_row: params[:display_summary_row].present?,
|
||||
display_header_row: params[:display_header_row].present?
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,6 +34,7 @@ module Spree
|
||||
link = link_to_with_icon(options[:icon], titleized_label, destination_url)
|
||||
css_classes << 'tab-with-icon'
|
||||
else
|
||||
titleized_label = raw("<span class='text'>#{titleized_label}</span>")
|
||||
link = link_to(titleized_label, destination_url)
|
||||
end
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ class Enterprise < ApplicationRecord
|
||||
# The next Rails version will have named variants but we need to store them
|
||||
# ourselves for now.
|
||||
LOGO_SIZES = {
|
||||
thumb: { resize_to_limit: [100, 100] },
|
||||
small: { resize_to_limit: [180, 180] },
|
||||
medium: { resize_to_limit: [300, 300] },
|
||||
thumb: { gravity: "Center", resize: "100x100^", crop: '100x100+0+0' },
|
||||
small: { gravity: "Center", resize: "180x180^", crop: '180x180+0+0' },
|
||||
medium: { gravity: "Center", resize: "300x300^", crop: '300x300+0+0' },
|
||||
}.freeze
|
||||
PROMO_IMAGE_SIZES = {
|
||||
thumb: { resize_to_limit: [100, 100] },
|
||||
|
||||
6
app/models/report_rendering_options.rb
Normal file
6
app/models/report_rendering_options.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ReportRenderingOptions < ApplicationRecord
|
||||
belongs_to :user, class_name: "Spree::User"
|
||||
serialize :options, Hash
|
||||
end
|
||||
@@ -37,7 +37,7 @@ module Spree
|
||||
foreign_key: :owner_id, inverse_of: :owner
|
||||
has_many :customers
|
||||
has_many :credit_cards
|
||||
|
||||
has_many :report_rendering_options, class_name: "::ReportRenderingOptions", dependent: :destroy
|
||||
accepts_nested_attributes_for :enterprise_roles, allow_destroy: true
|
||||
|
||||
accepts_nested_attributes_for :bill_address
|
||||
|
||||
20
app/queries/batch_taggable_tags_query.rb
Normal file
20
app/queries/batch_taggable_tags_query.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class BatchTaggableTagsQuery
|
||||
def self.call(taggables)
|
||||
::ActsAsTaggableOn::Tag.
|
||||
joins(:taggings).
|
||||
includes(:taggings).
|
||||
where(taggings:
|
||||
{
|
||||
taggable_type: taggables.model.to_s,
|
||||
taggable_id: taggables,
|
||||
context: 'tags'
|
||||
}).order("tags.name").each_with_object({}) do |tag, indexed_hash|
|
||||
tag.taggings.each do |tagging|
|
||||
indexed_hash[tagging.taggable_id] ||= []
|
||||
indexed_hash[tagging.taggable_id] << tag.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
38
app/reflexes/example_reflex.rb
Normal file
38
app/reflexes/example_reflex.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ExampleReflex < ApplicationReflex
|
||||
# Add Reflex methods in this file.
|
||||
#
|
||||
# All Reflex instances include CableReady::Broadcaster and expose the following properties:
|
||||
#
|
||||
# - connection - the ActionCable connection
|
||||
# - channel - the ActionCable channel
|
||||
# - request - an ActionDispatch::Request proxy for the socket connection
|
||||
# - session - the ActionDispatch::Session store for the current visitor
|
||||
# - flash - the ActionDispatch::Flash::FlashHash for the current request
|
||||
# - url - the URL of the page that triggered the reflex
|
||||
# - params - parameters from the element's closest form (if any)
|
||||
# - element - a Hash like object that represents the HTML element that triggered the reflex
|
||||
# - signed - use a signed Global ID to map dataset attribute to a model
|
||||
# eg. element.signed[:foo]
|
||||
# - unsigned - use an unsigned Global ID to map dataset attribute to a model
|
||||
# eg. element.unsigned[:foo]
|
||||
# - cable_ready - a special cable_ready that can broadcast to the current visitor
|
||||
# (no brackets needed)
|
||||
# - reflex_id - a UUIDv4 that uniquely identies each Reflex
|
||||
# - tab_id - a UUIDv4 that uniquely identifies the browser tab
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# before_reflex do
|
||||
# # throw :abort # this will prevent the Reflex from continuing
|
||||
# # learn more about callbacks at https://docs.stimulusreflex.com/rtfm/lifecycle
|
||||
# end
|
||||
#
|
||||
# def example(argument=true)
|
||||
# # Your logic here...
|
||||
# # Any declared instance variables will be made available to the Rails controller and view.
|
||||
# end
|
||||
#
|
||||
# Learn more at: https://docs.stimulusreflex.com/rtfm/reflex-classes
|
||||
end
|
||||
@@ -42,12 +42,18 @@ module Api
|
||||
.visible_variants_for_outgoing_exchanges_to(object.receiver)
|
||||
end
|
||||
|
||||
def preloaded_tag_list
|
||||
return object.tag_list unless options[:preloaded_tags]
|
||||
|
||||
options.dig(:preloaded_tags, object.id) || []
|
||||
end
|
||||
|
||||
def tag_list
|
||||
object.tag_list.join(",")
|
||||
preloaded_tag_list.join(",")
|
||||
end
|
||||
|
||||
def tags
|
||||
object.tag_list.map{ |t| { text: t } }
|
||||
preloaded_tag_list.map { |tag| { text: tag } }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,7 +34,8 @@ module Api
|
||||
|
||||
ActiveModel::ArraySerializer.
|
||||
new(scoped_exchanges, each_serializer: Api::Admin::ExchangeSerializer,
|
||||
current_user: options[:current_user])
|
||||
current_user: options[:current_user],
|
||||
preloaded_tags: BatchTaggableTagsQuery.call(scoped_exchanges))
|
||||
end
|
||||
|
||||
def editable_variants_for_incoming_exchanges
|
||||
|
||||
@@ -30,8 +30,14 @@ class OrderAvailablePaymentMethods
|
||||
distributor.payment_methods
|
||||
else
|
||||
distributor.payment_methods.where(
|
||||
id: order_cycle.distributor_payment_methods.select(:payment_method_id)
|
||||
id: available_distributor_payment_methods_ids
|
||||
)
|
||||
end.available.select(&:configured?)
|
||||
end
|
||||
|
||||
def available_distributor_payment_methods_ids
|
||||
order_cycle.distributor_payment_methods
|
||||
.where(distributor_id: distributor.id)
|
||||
.select(:payment_method_id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,8 +30,14 @@ class OrderAvailableShippingMethods
|
||||
distributor.shipping_methods
|
||||
else
|
||||
distributor.shipping_methods.where(
|
||||
id: order_cycle.distributor_shipping_methods.select(:shipping_method_id)
|
||||
id: available_distributor_shipping_methods_ids
|
||||
)
|
||||
end.frontend.to_a
|
||||
end
|
||||
|
||||
def available_distributor_shipping_methods_ids
|
||||
order_cycle.distributor_shipping_methods
|
||||
.where(distributor_id: distributor.id)
|
||||
.select(:shipping_method_id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -102,7 +102,6 @@
|
||||
= af.label :latitude, t(:latitude)
|
||||
\/
|
||||
= af.label :longitude, t(:longitude)
|
||||
%span.required *
|
||||
%div{'ofn-with-tip' => t('latitude_longitude_tip')}
|
||||
%a= t('admin.whats_this')
|
||||
.four.columns
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
= af.label :latitude, t(:latitude)
|
||||
\/
|
||||
= af.label :longitude, t(:longitude)
|
||||
%span.required *
|
||||
= render partial: 'admin/shared/tooltip', locals: {tooltip_text: t('latitude_longitude_tip')}
|
||||
.four.columns
|
||||
= af.text_field :latitude, { placeholder: t(:latitude_placeholder) }
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
%br
|
||||
100 x 100 pixels
|
||||
.omega.eight.columns
|
||||
%img{ class: 'image-field-group__preview-image', ng: { src: '{{ Enterprise.logo.medium }}', if: 'Enterprise.logo' } }
|
||||
%img{ class: 'image-field-group__preview-image', ng: { src: '{{ Enterprise.logo.thumb }}', if: 'Enterprise.logo' } }
|
||||
%br
|
||||
= f.file_field :logo
|
||||
%a.button.red{ href: '', ng: {click: 'removeLogo()', if: 'Enterprise.logo'} }
|
||||
= t('.remove_logo')
|
||||
|
||||
8
app/views/admin/products/index.html.haml
Normal file
8
app/views/admin/products/index.html.haml
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
- content_for :page_title do
|
||||
= t('admin.products_page.title')
|
||||
|
||||
= render partial: 'spree/admin/shared/product_sub_menu'
|
||||
|
||||
#products_page
|
||||
= render(ProductsTableComponent.new(user: spree_current_user))
|
||||
@@ -4,7 +4,7 @@
|
||||
.row.date-range-filter
|
||||
.alpha.two.columns= label_tag nil, t(:date_range)
|
||||
.omega.fourteen.columns
|
||||
= f.text_field "#{field}_gt", :class => 'datetimepicker datepicker-from', :placeholder => t(:start), data: { controller: "flatpickr", "flatpickr-enable-time-value": true }
|
||||
= f.text_field "#{field}_gt", :class => 'datetimepicker datepicker-from', :placeholder => t(:start), data: { controller: "flatpickr", "flatpickr-enable-time-value": true, "flatpickr-default-date-value": "startOfDay" }
|
||||
%span.range-divider
|
||||
%i.icon-arrow-right
|
||||
= f.text_field "#{field}_lt", :class => 'datetimepicker datepicker-to', :placeholder => t(:stop), data: { controller: "flatpickr", "flatpickr-enable-time-value": true }
|
||||
= f.text_field "#{field}_lt", :class => 'datetimepicker datepicker-to', :placeholder => t(:stop), data: { controller: "flatpickr", "flatpickr-enable-time-value": true, "flatpickr-default-date-value": "endOfDay" }
|
||||
|
||||
@@ -16,16 +16,16 @@
|
||||
.omega.fourteen.columns
|
||||
- if @report.header_option?
|
||||
%span.inline-checkbox{ style: "margin-right: 1rem;" }
|
||||
= check_box_tag :display_header_row, true, params[:display_header_row]
|
||||
= check_box_tag :display_header_row, true, @rendering_options.options[:display_header_row]
|
||||
= label_tag :display_header_row, t(".header_row")
|
||||
- if @report.summary_row_option?
|
||||
%span.inline-checkbox
|
||||
= check_box_tag :display_summary_row, true, params[:display_summary_row], { "data-csv-select-target": "checkbox" }
|
||||
= check_box_tag :display_summary_row, true, @rendering_options.options[:display_summary_row], { "data-csv-select-target": "checkbox" }
|
||||
= label_tag :display_summary_row, t(".summary_row"), { "data-csv-select-target": "label" }
|
||||
|
||||
- if @report.available_headers.present?
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:report_columns)
|
||||
.omega.fourteen.columns
|
||||
= render MultipleCheckedSelectComponent.new(name: "fields_to_show", options: @report.available_headers, selected: @params_fields_to_show)
|
||||
= render MultipleCheckedSelectComponent.new(name: "fields_to_show", options: @report.available_headers, selected: @rendering_options.options[:fields_to_show])
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
= render 'admin/reports/date_range_form', f: f
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:report_hubs)
|
||||
.omega.fourteen.columns= f.collection_select(:distributor_id_in, @data.orders_distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
%a{"ng-click" => "triggerProductModal()"}
|
||||
%span.product-thumb__bulk-label{"ng-if" => "::product.group_buy"}
|
||||
= t(".bulk")
|
||||
%img{"ng-src" => "{{::product.primaryImageOrMissing}}", loading: "lazy"}
|
||||
%img{"ng-src" => "{{::product.primaryImageOrMissing}}"}
|
||||
|
||||
.summary
|
||||
.summary-header
|
||||
|
||||
@@ -54,6 +54,6 @@
|
||||
.actions.filter-actions
|
||||
%a.button.icon-search{'ng-click' => 'fetchResults()'}
|
||||
= t(:filter_results)
|
||||
%a.button{'ng-click' => 'clearFilters()', "id": "clear_filters_button"}
|
||||
%a.button{'ng-click' => 'clearFilters()', "id": "clear_filters_button", "class": ("secondary" if feature?(:admin_style_v2, spree_current_user)) }
|
||||
= t(:clear_filters)
|
||||
|
||||
|
||||
@@ -95,17 +95,17 @@
|
||||
%td.align-center
|
||||
%span{'ng-bind-html' => 'order.display_total'}
|
||||
%td.actions
|
||||
%div.row-loading-icons
|
||||
%div{ng: {show: 'rowStatus[order.id] == "loading"', cloak: true}, style: "width: 30px; height: 30px;"}
|
||||
= render partial: "components/spinner"
|
||||
%i.success.icon-ok-sign{ng: {show: 'rowStatus[order.id] == "success"'} }
|
||||
%i.error.icon-remove-sign.with-tip{ng: {show: 'rowStatus[order.id] == "error"'}, 'ofn-with-tip' => t('.order_not_updated')}
|
||||
%a.icon_link.with-tip.icon-edit.no-text{'ng-href' => '{{order.edit_path}}', 'data-action' => 'edit', 'ofn-with-tip' => t('.edit')}
|
||||
%div{'ng-if' => 'order.ready_to_ship'}
|
||||
%button.icon-road.icon_link.with-tip.no-text{'ng-click' => 'shipOrder(order)', rel: 'nofollow', 'ofn-with-tip' => t('.ship')}
|
||||
%div{'ng-if' => 'order.ready_to_capture'}
|
||||
%button.icon-capture.icon_link.no-text{'ng-click' => 'capturePayment(order)', rel: 'nofollow', 'ofn-with-tip' => t('.capture')}
|
||||
|
||||
.flex
|
||||
%div.row-loading-icons
|
||||
%div{ng: {show: 'rowStatus[order.id] == "loading"', cloak: true}, style: "width: 30px; height: 30px;"}
|
||||
= render partial: "components/spinner"
|
||||
%i.success.icon-ok-sign{ng: {show: 'rowStatus[order.id] == "success"'} }
|
||||
%i.error.icon-remove-sign.with-tip{ng: {show: 'rowStatus[order.id] == "error"'}, 'ofn-with-tip' => t('.order_not_updated')}
|
||||
%a.icon_link.with-tip.icon-edit.no-text{'ng-href' => '{{order.edit_path}}', 'data-action' => 'edit', 'ofn-with-tip' => t('.edit')}
|
||||
%div{'ng-if' => 'order.ready_to_ship'}
|
||||
%button.icon-road.icon_link.with-tip.no-text{'ng-click' => 'shipOrder(order)', rel: 'nofollow', 'ofn-with-tip' => t('.ship')}
|
||||
%div{'ng-if' => 'order.ready_to_capture'}
|
||||
%button.icon-capture.icon_link.no-text{'ng-click' => 'capturePayment(order)', rel: 'nofollow', 'ofn-with-tip' => t('.capture')}
|
||||
.sixteen.columns.alpha#loading{ 'ng-show' => 'RequestMonitor.loading' }
|
||||
= render partial: "components/admin_spinner"
|
||||
%h1
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
= csrf_meta_tags
|
||||
= action_cable_meta_tag
|
||||
|
||||
= action_cable_meta_tag
|
||||
|
||||
%title
|
||||
- if content_for? :html_title
|
||||
= yield :html_title
|
||||
@@ -10,7 +12,7 @@
|
||||
= t(controller.controller_name, :default => controller.controller_name.titleize)
|
||||
= " - OFN #{t(:administration)}"
|
||||
|
||||
%link{:href => "https://fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,400,600&subset=latin,cyrillic,greek,vietnamese", :rel => "stylesheet", :type => "text/css"}
|
||||
%link{:href => "https://fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,400,600,700&subset=latin,cyrillic,greek,vietnamese", :rel => "stylesheet", :type => "text/css"}
|
||||
|
||||
= stylesheet_pack_tag 'admin-styles', media: "screen, print"
|
||||
= render "layouts/bugsnag_js"
|
||||
|
||||
@@ -4,3 +4,5 @@
|
||||
= tab :properties
|
||||
= tab :variant_overrides, url: main_app.admin_inventory_path, match_path: '/inventory'
|
||||
= tab :import, url: main_app.admin_product_import_path, match_path: '/product_import'
|
||||
- if feature?(:new_products_page, spree_current_user)
|
||||
= tab :new_products, url: main_app.admin_new_products_path, match_path: '/new_products'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
%head{"data-hook" => "admin_inside_head"}
|
||||
= render :partial => 'spree/admin/shared/head'
|
||||
|
||||
%body.admin
|
||||
%body.admin{ "class": ("admin-v2" if feature?(:admin_style_v2, spree_current_user)) }
|
||||
- if content_for?(:main_ng_app_name)
|
||||
- if content_for?(:main_ng_ctrl_name)
|
||||
%div{ "ng-app" => yield(:main_ng_app_name).strip.html_safe, "ng-controller" => yield(:main_ng_ctrl_name).strip.html_safe }
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
import { Controller } from "stimulus";
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["reportType", "checkbox", "label"]
|
||||
static targets = ["reportType", "checkbox", "label"];
|
||||
|
||||
handleSelectChange() {
|
||||
this.reportTypeTarget.value == "csv" ? this.disableField() : this.enableField()
|
||||
this.reportTypeTarget.value == "csv"
|
||||
? this.disableField()
|
||||
: this.enableField();
|
||||
}
|
||||
|
||||
disableField() {
|
||||
this.checkboxTarget.checked = false;
|
||||
this.checkboxTarget.disabled = true;
|
||||
this.labelTarget.classList.add("disabled");
|
||||
if (this.hasCheckboxTarget) {
|
||||
this.checkboxTarget.checked = false;
|
||||
this.checkboxTarget.disabled = true;
|
||||
}
|
||||
if (this.hasLabelTarget) {
|
||||
this.labelTarget.classList.add("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
enableField() {
|
||||
this.checkboxTarget.checked = true;
|
||||
this.checkboxTarget.disabled = false;
|
||||
this.labelTarget.classList.remove("disabled");
|
||||
if (this.hasCheckboxTarget) {
|
||||
this.checkboxTarget.checked = true;
|
||||
this.checkboxTarget.disabled = false;
|
||||
}
|
||||
if (this.hasLabelTarget) {
|
||||
this.labelTarget.classList.remove("disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@ import ShortcutButtonsPlugin from "shortcut-buttons-flatpickr";
|
||||
import labelPlugin from "flatpickr/dist/plugins/labelPlugin/labelPlugin";
|
||||
|
||||
export default class extends Flatpickr {
|
||||
static values = { enableTime: Boolean, mode: String };
|
||||
/*
|
||||
* defaultDate (optional): "startOfDay" | "endOfDay"
|
||||
*/
|
||||
static values = { enableTime: Boolean, mode: String, defaultDate: String };
|
||||
static targets = ["start", "end"];
|
||||
locales = {
|
||||
ar: ar,
|
||||
@@ -60,6 +63,9 @@ export default class extends Flatpickr {
|
||||
|
||||
open() {
|
||||
this.fp.element.dispatchEvent(new Event("focus"));
|
||||
if (!this.fp.selectedDates.length) {
|
||||
this.setDefaultDateValue();
|
||||
}
|
||||
}
|
||||
onChangeEvent(e) {
|
||||
if (
|
||||
@@ -136,4 +142,16 @@ export default class extends Flatpickr {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
setDefaultDateValue() {
|
||||
if (this.defaultDateValue === "startOfDay") {
|
||||
this.fp.setDate(moment().startOf("day").format());
|
||||
} else if (this.defaultDateValue === "endOfDay") {
|
||||
/*
|
||||
* We use "startOf('day')" of tomorrow in order to not lose
|
||||
* the records between [23:59:00 ~ 23:59:59] of today
|
||||
*/
|
||||
this.fp.setDate(moment().add(1, "days").startOf("day").format());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
app/webpacker/controllers/index.js
Normal file
16
app/webpacker/controllers/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// Load all the controllers within this directory and all subdirectories.
|
||||
// Controller files must be named *_controller.js.
|
||||
import { Application } from "stimulus";
|
||||
import { definitionsFromContext } from "stimulus/webpack-helpers";
|
||||
import StimulusReflex from "stimulus_reflex";
|
||||
import consumer from "../channels/consumer";
|
||||
import controller from "../controllers/application_controller";
|
||||
import CableReady from "cable_ready";
|
||||
|
||||
const application = Application.start();
|
||||
const context = require.context("controllers", true, /_controller\.js$/);
|
||||
application.load(definitionsFromContext(context));
|
||||
application.consumer = consumer;
|
||||
StimulusReflex.initialize(application, { controller, isolate: true });
|
||||
StimulusReflex.debug = process.env.RAILS_ENV === "development";
|
||||
CableReady.initialize({ consumer });
|
||||
46
app/webpacker/controllers/products_table_controller.js
Normal file
46
app/webpacker/controllers/products_table_controller.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import ApplicationController from "./application_controller";
|
||||
|
||||
export default class extends ApplicationController {
|
||||
connect() {
|
||||
super.connect();
|
||||
document.addEventListener(
|
||||
"stimulus-reflex:before",
|
||||
this.handleBeforeReflex.bind(this)
|
||||
);
|
||||
document.addEventListener(
|
||||
"stimulus-reflex:after",
|
||||
this.handleAfterReflex.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
super.disconnect();
|
||||
document.removeEventListener(
|
||||
"stimulus-reflex:before",
|
||||
this.handleBeforeReflex.bind(this)
|
||||
);
|
||||
document.removeEventListener(
|
||||
"stimulus-reflex:after",
|
||||
this.handleAfterReflex.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
handleBeforeReflex(event) {
|
||||
if (event.detail.reflex.indexOf("ProductsTableComponent#") !== -1) {
|
||||
this.showLoading();
|
||||
}
|
||||
}
|
||||
|
||||
handleAfterReflex(event) {
|
||||
if (event.detail.reflex.indexOf("ProductsTableComponent#") !== -1) {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
this.element.classList.add("loading");
|
||||
}
|
||||
hideLoading() {
|
||||
this.element.classList.remove("loading");
|
||||
}
|
||||
}
|
||||
28
app/webpacker/controllers/search-input_controller.js
Normal file
28
app/webpacker/controllers/search-input_controller.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import ApplicationController from "./application_controller";
|
||||
|
||||
export default class extends ApplicationController {
|
||||
connect() {
|
||||
super.connect();
|
||||
this.element
|
||||
.querySelector("input")
|
||||
.addEventListener("keydown", this.searchOnEnter);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
super.disconnect();
|
||||
this.element
|
||||
.querySelector("input")
|
||||
.removeEventListener("keydown", this.searchOnEnter);
|
||||
}
|
||||
|
||||
searchOnEnter = (e) => {
|
||||
if (e.key === "Enter") {
|
||||
this.element.querySelector(".search-button").click();
|
||||
}
|
||||
};
|
||||
|
||||
search(e) {
|
||||
this.element.querySelector(".search-button").dataset["value"] =
|
||||
e.target.value;
|
||||
}
|
||||
}
|
||||
52
app/webpacker/controllers/selector_controller.js
Normal file
52
app/webpacker/controllers/selector_controller.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import ApplicationController from "./application_controller";
|
||||
|
||||
export default class extends ApplicationController {
|
||||
connect() {
|
||||
super.connect();
|
||||
window.addEventListener("click", this.closeOnClickOutside);
|
||||
this.computeItemsHeight();
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
super.disconnect();
|
||||
window.removeEventListener("click", this.closeOnClickOutside);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.close();
|
||||
}
|
||||
|
||||
afterReflex() {
|
||||
this.computeItemsHeight();
|
||||
}
|
||||
|
||||
toggle = (event) => {
|
||||
event.preventDefault();
|
||||
this.element.querySelector(".selector").classList.toggle("selector-close");
|
||||
};
|
||||
|
||||
// Private
|
||||
closeOnClickOutside = (event) => {
|
||||
if (
|
||||
!this.element.contains(event.target) &&
|
||||
this.isVisible(this.element.querySelector(".selector-wrapper"))
|
||||
) {
|
||||
this.close();
|
||||
}
|
||||
};
|
||||
|
||||
computeItemsHeight = () => {
|
||||
const items = this.element.querySelector(".selector-items");
|
||||
const rect = items.getBoundingClientRect();
|
||||
items.style.maxHeight = `calc(100vh - ${rect.height}px)`;
|
||||
};
|
||||
|
||||
isVisible = (element) => {
|
||||
const style = window.getComputedStyle(element);
|
||||
return style.display !== "none" && style.visibility !== "hidden";
|
||||
};
|
||||
|
||||
close = () => {
|
||||
this.element.querySelector(".selector").classList.add("selector-close");
|
||||
};
|
||||
}
|
||||
15
app/webpacker/controllers/selector_with_filter_controller.js
Normal file
15
app/webpacker/controllers/selector_with_filter_controller.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import SelectorController from "./selector_controller";
|
||||
|
||||
export default class extends SelectorController {
|
||||
static targets = ["items"];
|
||||
|
||||
filter = (event) => {
|
||||
const query = event.target.value;
|
||||
|
||||
this.itemsTargets.forEach((el, i) => {
|
||||
el.style.display = el.textContent.toLowerCase().includes(query)
|
||||
? ""
|
||||
: "none";
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -15,7 +15,7 @@ export default class extends Controller {
|
||||
this.containerTarget.style.bottom = "-1px";
|
||||
const observer = new IntersectionObserver(
|
||||
([e]) => {
|
||||
e.target.classList.toggle("sticked", e.intersectionRatio < 1);
|
||||
e.target.classList.toggle("sticked", e.intersectionRatio <= 1);
|
||||
},
|
||||
{ threshold: [1] }
|
||||
);
|
||||
|
||||
@@ -120,3 +120,12 @@
|
||||
@import "components/tom_select";
|
||||
|
||||
@import 'app/components/help_modal_component/help_modal_component';
|
||||
@import "app/components/product_component/product_component";
|
||||
@import "app/components/selector_component/selector_component";
|
||||
@import "app/components/products_table_component/products_table_component";
|
||||
@import "app/components/selector_with_filter_component/selector_with_filter_component";
|
||||
@import "app/components/pagination_component/pagination_component";
|
||||
@import "app/components/table_header_component/table_header_component";
|
||||
@import "app/components/search_input_component/search_input_component";
|
||||
|
||||
@import "v2/main.scss";
|
||||
|
||||
@@ -78,7 +78,6 @@ nav.menu {
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
|
||||
i {
|
||||
display: inline;
|
||||
|
||||
66
app/webpacker/css/admin/v2/components/buttons.scss
Normal file
66
app/webpacker/css/admin/v2/components/buttons.scss
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Overide buttons.scss app/webpacker/css/admin/components/buttons.scss */
|
||||
|
||||
@mixin backgroundAndBorder($color) {
|
||||
background-color: $color;
|
||||
border: 2px solid $color;
|
||||
}
|
||||
|
||||
input[type="submit"],
|
||||
select[type="submit"],
|
||||
.select2-container-multi [type="submit"].select2-choices,
|
||||
input[type="button"],
|
||||
select[type="button"],
|
||||
.select2-container-multi [type="button"].select2-choices,
|
||||
button,
|
||||
.button,
|
||||
.actions a:not([class*="icon-"]),
|
||||
.admin__section-header .ofn-drop-down // Same behavior as the button
|
||||
{
|
||||
&.disabled,
|
||||
&[disabled] {
|
||||
@include backgroundAndBorder($v2-light-grey);
|
||||
}
|
||||
|
||||
&:not(.disabled):not([disabled]):not(.secondary) {
|
||||
// Change the color of the button only if it's not disabled
|
||||
@include backgroundAndBorder($v2-blue-light);
|
||||
|
||||
&:hover {
|
||||
@include backgroundAndBorder($v2-blue);
|
||||
box-shadow: $v2-box-shadow;
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background-color: $white;
|
||||
border: 2px solid $v2-blue-light;
|
||||
color: $v2-blue-light;
|
||||
|
||||
&:hover {
|
||||
background-color: $v2-blue-lightest;
|
||||
color: $v2-blue;
|
||||
box-shadow: $v2-box-shadow;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
@include backgroundAndBorder($v2-blue);
|
||||
}
|
||||
}
|
||||
|
||||
#table-filter .actions {
|
||||
/* used to draw a line on the right and left of the actions buttons
|
||||
We can then remove the dropshadow on the buttons */
|
||||
&:before,
|
||||
&:after {
|
||||
background-color: $v2-light-grey; // same color as the border of the fieldset. see forms.scss
|
||||
height: 1px;
|
||||
content: attr(data-initials);
|
||||
flex-grow: 1; // make the line as long as it can
|
||||
}
|
||||
}
|
||||
|
||||
#table-filter fieldset:has(.actions) {
|
||||
// do not apply border to filter actions as it's drawn by the #table-filter .actions before and after pseudo elements
|
||||
border-bottom: 0;
|
||||
}
|
||||
9
app/webpacker/css/admin/v2/components/progress.scss
Normal file
9
app/webpacker/css/admin/v2/components/progress.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
/* Overide app/webpacker/css/admin/components/progress.scss file */
|
||||
|
||||
#progress {
|
||||
background-color: $v2-blue;
|
||||
}
|
||||
|
||||
#loading {
|
||||
color: $v2-blue;
|
||||
}
|
||||
73
app/webpacker/css/admin/v2/components/tables.scss
Normal file
73
app/webpacker/css/admin/v2/components/tables.scss
Normal file
@@ -0,0 +1,73 @@
|
||||
/* Overide tables.scss app/webpacker/css/admin/components/tables.scss */
|
||||
|
||||
table thead th {
|
||||
background-color: $v2-medium-light-grey;
|
||||
border: none;
|
||||
color: $v2-blue;
|
||||
text-transform: capitalize;
|
||||
font-size: 13px;
|
||||
|
||||
a {
|
||||
border: none;
|
||||
color: $v2-blue;
|
||||
}
|
||||
}
|
||||
|
||||
table tbody tr {
|
||||
&:first-child th,
|
||||
&:first-child td {
|
||||
border-top: none; // Don't show the top border of the first row
|
||||
}
|
||||
td:not(:first-child) {
|
||||
border-left: none; // Only show left border on the first cells, as it indicates the order state by its color
|
||||
}
|
||||
|
||||
td {
|
||||
border-bottom: none; // By default, do not show the border of the cells
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
|
||||
border-bottom: 2px solid $v2-medium-light-grey;
|
||||
&.actions {
|
||||
border-bottom: 2px solid $v2-medium-light-grey !important; // needs to be important because of already defined with important
|
||||
}
|
||||
> .flex {
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
}
|
||||
}
|
||||
&.even,
|
||||
&.odd {
|
||||
td {
|
||||
background-color: transparent; // Do not use odd and even colors for background
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table th.actions,
|
||||
table td.actions {
|
||||
// Special for icons in the actions column
|
||||
[class*="icon-"].no-text {
|
||||
border: 2px solid $v2-blue-light;
|
||||
background-color: $v2-blue-lightest;
|
||||
|
||||
&:hover {
|
||||
border-color: $v2-blue;
|
||||
background-color: $v2-blue-light;
|
||||
box-shadow: $v2-box-shadow;
|
||||
|
||||
&:before {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table#listing_orders td {
|
||||
// When the table is the listing of orders, we need to increase the height of the cells
|
||||
padding: 20px 0;
|
||||
|
||||
&.actions {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
13
app/webpacker/css/admin/v2/main.scss
Normal file
13
app/webpacker/css/admin/v2/main.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
@import "variables.scss";
|
||||
@import "shared/typography.scss";
|
||||
|
||||
body.admin.admin-v2 {
|
||||
@import "navigation.scss";
|
||||
@import "plugins/select2.scss";
|
||||
@import "plugins/powertip.scss";
|
||||
@import "plugins/flatpickr-customization.scss";
|
||||
@import "shared/forms.scss";
|
||||
@import "components/buttons.scss";
|
||||
@import "components/tables.scss";
|
||||
@import "components/progress.scss";
|
||||
}
|
||||
62
app/webpacker/css/admin/v2/navigation.scss
Normal file
62
app/webpacker/css/admin/v2/navigation.scss
Normal file
@@ -0,0 +1,62 @@
|
||||
#header {
|
||||
#login-nav {
|
||||
li {
|
||||
color: $v2-medium-dark-grey;
|
||||
|
||||
i {
|
||||
@include v2-link-color();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#admin-menu {
|
||||
background-color: $v2-orange;
|
||||
|
||||
li a span.text {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
li.tab-with-icon a:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
li a:hover,
|
||||
li.selected a {
|
||||
background-color: $v2-orange-light;
|
||||
|
||||
span {
|
||||
text-shadow: 1px 1px 9px $v2-orange;
|
||||
}
|
||||
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#sub-menu {
|
||||
background-color: $v2-orange-light;
|
||||
|
||||
li a {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
li a:hover,
|
||||
li.selected a {
|
||||
text-shadow: 1px 1px 9px $v2-orange;
|
||||
background-color: lighten($v2-orange-light, 7%);
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin__section-header {
|
||||
background-color: transparent;
|
||||
border-bottom: none;
|
||||
|
||||
h1.js-admin-page-title {
|
||||
color: $v2-medium-dark-grey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Override flatpickr styles: app/webpacker/css/admin/plugins/flatpickr-customization.scss */
|
||||
|
||||
.flatpickr-calendar {
|
||||
&.arrowTop::after {
|
||||
border-bottom-color: $v2-blue-light;
|
||||
}
|
||||
.flatpickr-months .flatpickr-month,
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months {
|
||||
background: $v2-blue-light;
|
||||
color: white;
|
||||
input {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.flatpickr-weekdays {
|
||||
background: $v2-blue-light;
|
||||
|
||||
.flatpickr-weekday {
|
||||
background: $v2-blue-light;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.flatpickr-day.selected,
|
||||
.flatpickr-day.startRange,
|
||||
.flatpickr-day.endRange,
|
||||
.flatpickr-day.selected.inRange,
|
||||
.flatpickr-day.startRange.inRange,
|
||||
.flatpickr-day.endRange.inRange,
|
||||
.flatpickr-day.selected:focus,
|
||||
.flatpickr-day.startRange:focus,
|
||||
.flatpickr-day.endRange:focus,
|
||||
.flatpickr-day.selected:hover,
|
||||
.flatpickr-day.startRange:hover,
|
||||
.flatpickr-day.endRange:hover,
|
||||
.flatpickr-day.selected.prevMonthDay,
|
||||
.flatpickr-day.startRange.prevMonthDay,
|
||||
.flatpickr-day.endRange.prevMonthDay,
|
||||
.flatpickr-day.selected.nextMonthDay,
|
||||
.flatpickr-day.startRange.nextMonthDay,
|
||||
.flatpickr-day.endRange.nextMonthDay {
|
||||
background: $v2-blue-light;
|
||||
border-color: $v2-blue-light;
|
||||
}
|
||||
}
|
||||
31
app/webpacker/css/admin/v2/plugins/powertip.scss
Normal file
31
app/webpacker/css/admin/v2/plugins/powertip.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
/* Overide file powertip.scss app/webpacker/css/admin/v2/plugins/powertip.scss */
|
||||
|
||||
#powerTip {
|
||||
background-color: $v2-blue;
|
||||
|
||||
&.n:before,
|
||||
&.ne:before,
|
||||
&.nw:before {
|
||||
border-top-color: $v2-blue;
|
||||
}
|
||||
|
||||
&.e:before {
|
||||
border-right-color: $v2-blue;
|
||||
}
|
||||
&.s:before,
|
||||
&.se:before,
|
||||
&.sw:before {
|
||||
border-bottom-color: $v2-blue;
|
||||
}
|
||||
&.w:before {
|
||||
border-left-color: $v2-blue;
|
||||
}
|
||||
&.ne:before,
|
||||
&.se:before {
|
||||
border-right-color: $v2-blue;
|
||||
}
|
||||
&.nw:before,
|
||||
&.sw:before {
|
||||
border-right-color: $v2-blue;
|
||||
}
|
||||
}
|
||||
70
app/webpacker/css/admin/v2/plugins/select2.scss
Normal file
70
app/webpacker/css/admin/v2/plugins/select2.scss
Normal file
@@ -0,0 +1,70 @@
|
||||
/* Override select2 styles app/webpacker/css/admin/plugins/select2.scss */
|
||||
|
||||
.select2-container {
|
||||
.select2-choice {
|
||||
background-color: transparent;
|
||||
border: 1px solid $v2-light-grey !important;
|
||||
color: $v2-medium-grey !important;
|
||||
padding-left: 5px;
|
||||
font-size: 13px;
|
||||
padding-top: 3px;
|
||||
|
||||
.select2-arrow {
|
||||
color: $v2-medium-grey;
|
||||
}
|
||||
}
|
||||
|
||||
&.select2-container-active,
|
||||
&:hover {
|
||||
.select2-choice {
|
||||
background-color: transparent !important;
|
||||
border-color: $v2-medium-grey !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.select2-dropdown-open.select2-container-active {
|
||||
&:not(.select2-drop-above) {
|
||||
.select2-choice {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
&.select2-drop-above .select.select2-choice {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select2-container-multi {
|
||||
.select2-choices {
|
||||
border-color: $v2-medium-grey !important;
|
||||
.select2-search-choice {
|
||||
background-color: $v2-blue-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select2-drop {
|
||||
border-color: $v2-medium-grey;
|
||||
}
|
||||
|
||||
.select2-search {
|
||||
color: $v2-medium-grey;
|
||||
}
|
||||
|
||||
.select2-results {
|
||||
margin-right: 0;
|
||||
li {
|
||||
.select2-result-label {
|
||||
color: $v2-medium-grey;
|
||||
}
|
||||
&.select2-highlighted {
|
||||
background-color: $v2-blue;
|
||||
}
|
||||
&.select2-no-results,
|
||||
&.select2-searching {
|
||||
color: $v2-medium-grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
app/webpacker/css/admin/v2/shared/forms.scss
Normal file
55
app/webpacker/css/admin/v2/shared/forms.scss
Normal file
@@ -0,0 +1,55 @@
|
||||
/* Overide forms.scss app/webpacker/css/admin/shared/forms.scss */
|
||||
|
||||
input[type="text"],
|
||||
.select2-container-multi .select2-choices,
|
||||
.select2-search input,
|
||||
.select2-search select,
|
||||
select,
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="url"],
|
||||
input[type="tel"],
|
||||
input[type="date"],
|
||||
input[type="datetime"],
|
||||
input[type="time"],
|
||||
input[type="number"],
|
||||
textarea,
|
||||
fieldset {
|
||||
// Change the color of all inputs
|
||||
color: $v2-medium-grey;
|
||||
border-color: $v2-light-grey !important;
|
||||
|
||||
&:focus {
|
||||
border-color: $v2-medium-grey;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset label {
|
||||
color: $v2-medium-grey;
|
||||
}
|
||||
|
||||
fieldset legend {
|
||||
color: $v2-blue;
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
accent-color: $v2-blue;
|
||||
}
|
||||
|
||||
fieldset .filter-actions {
|
||||
button,
|
||||
.button,
|
||||
input[type="submit"] {
|
||||
box-shadow: none;
|
||||
width: 200px; // adjust at the same size in order to center them. Not ideal but works for now.
|
||||
|
||||
&:first-of-type {
|
||||
margin-right: 0; // don't see any reason to have a margin right on the first button as it's managed by the flexbox
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: $v2-box-shadow;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
app/webpacker/css/admin/v2/shared/typography.scss
Normal file
18
app/webpacker/css/admin/v2/shared/typography.scss
Normal file
@@ -0,0 +1,18 @@
|
||||
// Overide app/webpacker/css/admin/shared/typography.scss
|
||||
|
||||
@mixin v2-link-color() {
|
||||
color: $v2-blue;
|
||||
border-color: $v2-blue;
|
||||
&:hover {
|
||||
color: $v2-blue-dark;
|
||||
border-color: $v2-blue-dark;
|
||||
}
|
||||
}
|
||||
|
||||
body.admin.admin-v2 {
|
||||
color: $v2-body-grey;
|
||||
|
||||
a:not(.button) {
|
||||
@include v2-link-color();
|
||||
}
|
||||
}
|
||||
23
app/webpacker/css/admin/v2/variables.scss
Normal file
23
app/webpacker/css/admin/v2/variables.scss
Normal file
@@ -0,0 +1,23 @@
|
||||
// Should finally replace (or at least complete) the file /admin/globale/variables.scss
|
||||
|
||||
$v2-orange: #f27052;
|
||||
$v2-orange-light: #f5947d;
|
||||
$v2-orange-lighter: #f8b7a8;
|
||||
$v2-orange-lightest: #fcdbd4;
|
||||
|
||||
$v2-dark-grey: #333333;
|
||||
$v2-medium-dark-grey: #444444;
|
||||
$v2-body-grey: #666666;
|
||||
$v2-medium-grey: #717171;
|
||||
$v2-medium-light-grey: #e6e6e6;
|
||||
$v2-light-grey: #e7e7e7;
|
||||
|
||||
$v2-blue: #017a9a;
|
||||
$v2-blue-dark: #005e7a;
|
||||
$v2-blue-light: #0096ad;
|
||||
$v2-blue-lightest: #e6f7fa; // Could be used as a background color for the action icons
|
||||
|
||||
$v2-green: #019854;
|
||||
$v2-green-light: #01cb70;
|
||||
|
||||
$v2-box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); // Default box shadow for actions stuff
|
||||
@@ -13,3 +13,6 @@ application.consumer = consumer;
|
||||
StimulusReflex.initialize(application, { controller, isolate: true });
|
||||
StimulusReflex.debug = process.env.RAILS_ENV === "development";
|
||||
CableReady.initialize({ consumer });
|
||||
|
||||
import debounced from "debounced";
|
||||
debounced.initialize({ input: { wait: 300 } });
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
/* eslint no-console:0 */
|
||||
|
||||
// StimulusJS
|
||||
import { Application } from "stimulus";
|
||||
import { definitionsFromContext } from "stimulus/webpack-helpers";
|
||||
|
||||
const application = Application.start();
|
||||
const context = require.context("controllers", true, /.js$/);
|
||||
application.load(definitionsFromContext(context));
|
||||
|
||||
import CableReady from "cable_ready";
|
||||
import mrujs from "mrujs";
|
||||
import { CableCar } from "mrujs/plugins";
|
||||
@@ -23,11 +14,4 @@ require.context("../fonts", true);
|
||||
const images = require.context("../images", true);
|
||||
const imagePath = (name) => images(name, true);
|
||||
|
||||
import StimulusReflex from "stimulus_reflex";
|
||||
import consumer from "../channels/consumer";
|
||||
import controller from "../controllers/application_controller";
|
||||
|
||||
application.consumer = consumer;
|
||||
StimulusReflex.initialize(application, { controller, isolate: true });
|
||||
StimulusReflex.debug = process.env.RAILS_ENV === "development";
|
||||
CableReady.initialize({ consumer });
|
||||
import "controllers";
|
||||
|
||||
13
bin/knapsack_pro_rspec
Executable file
13
bin/knapsack_pro_rspec
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
if [ "$KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC" = "" ]; then
|
||||
KNAPSACK_PRO_ENDPOINT=https://api-disabled-for-fork.knapsackpro.com \
|
||||
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC=disabled-for-fork \
|
||||
KNAPSACK_PRO_MAX_REQUEST_RETRIES=0 \
|
||||
bundle exec rake knapsack_pro:rspec # use Regular Mode here always
|
||||
else
|
||||
# Regular Mode
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
|
||||
# or you can use Queue Mode instead of Regular Mode if you like
|
||||
# bundle exec rake knapsack_pro:queue:rspec
|
||||
fi
|
||||
@@ -155,15 +155,17 @@ module Openfoodnetwork
|
||||
|
||||
initializer "ofn.reports" do |app|
|
||||
module ::Reporting; end
|
||||
loader = Zeitwerk::Loader.new
|
||||
loader.push_dir("#{Rails.root}/lib/reporting", namespace: ::Reporting)
|
||||
loader.enable_reloading
|
||||
loader.setup
|
||||
loader.eager_load
|
||||
Rails.application.reloader.to_prepare do
|
||||
loader = Zeitwerk::Loader.new
|
||||
loader.push_dir("#{Rails.root}/lib/reporting", namespace: ::Reporting)
|
||||
loader.enable_reloading
|
||||
loader.setup
|
||||
loader.eager_load
|
||||
|
||||
if Rails.env.development?
|
||||
require 'listen'
|
||||
Listen.to("lib/reporting") { loader.reload }.start
|
||||
if Rails.env.development?
|
||||
require 'listen'
|
||||
Listen.to("lib/reporting") { loader.reload }.start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -247,5 +249,7 @@ module Openfoodnetwork
|
||||
config.active_storage.variable_content_types += ["image/svg+xml"]
|
||||
|
||||
config.exceptions_app = self.routes
|
||||
|
||||
config.autoloader = :zeitwerk
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
Openfoodnetwork::Application.configure do
|
||||
config.action_controller.default_url_options = {host: "localhost", port: 3000}
|
||||
# Settings specified here will take precedence over those in config/application.rb
|
||||
#
|
||||
# PROFILE switches several settings to a more "production-like" value
|
||||
@@ -23,6 +24,8 @@ Openfoodnetwork::Application.configure do
|
||||
}
|
||||
end
|
||||
|
||||
config.session_store :cache_store, key: "_sessions_development", compress: true, pool_size: 5, expire_after: 1.year
|
||||
|
||||
config.eager_load = false
|
||||
|
||||
# Show full error reports and disable caching
|
||||
|
||||
0
config/initializers/action_cable.rb
Normal file
0
config/initializers/action_cable.rb
Normal file
@@ -396,6 +396,25 @@ ar:
|
||||
destroy: "الغاء"
|
||||
rename: "إعادة تسمية"
|
||||
admin:
|
||||
products_page:
|
||||
title: منتجات
|
||||
filters:
|
||||
categories:
|
||||
title: التصنيفات
|
||||
producers:
|
||||
title: المنتجين
|
||||
colums: أعمدة
|
||||
columns:
|
||||
name: الاسم
|
||||
unit: وحدة
|
||||
price: السعر
|
||||
producer: المنتج
|
||||
category: الفئة
|
||||
columns_selector:
|
||||
unit: وحدة
|
||||
price: السعر
|
||||
producer: المنتج
|
||||
category: الفئة
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "لا يمكنك تغيير الطلب الذي تم إلغاؤه."
|
||||
begins_at: يبدأ عند
|
||||
@@ -4350,3 +4369,11 @@ ar:
|
||||
unprocessable_entity:
|
||||
title: "تم رفض التغيير الذي أردته (422)"
|
||||
message_html: "<p style=\";text-align:right;direction:rtl\">تم رفض التغيير الذي تريده. ربما حاولت تغيير شيء لا يمكنك الوصول إليه.<br><h3 style=\";text-align:right;direction:rtl\"> <a href='/' >العودة للصفحة الرئيسية</a></h3></p>"
|
||||
components:
|
||||
search_input:
|
||||
placeholder: بحث
|
||||
selector_with_filter:
|
||||
search_placeholder: بحث
|
||||
pagination:
|
||||
next: التالى
|
||||
previous: السابق
|
||||
|
||||
@@ -399,6 +399,25 @@ ca:
|
||||
destroy: "Eliminar"
|
||||
rename: "Reanomenar"
|
||||
admin:
|
||||
products_page:
|
||||
title: 'Productes '
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Productors
|
||||
colums: Columnes
|
||||
columns:
|
||||
name: Nom
|
||||
unit: Unitat
|
||||
price: Preu
|
||||
producer: Productora
|
||||
category: Categoria
|
||||
columns_selector:
|
||||
unit: Unitat
|
||||
price: Preu
|
||||
producer: Productora
|
||||
category: Categoria
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "No podeu canviar una comanda cancel·lada."
|
||||
begins_at: Comença a
|
||||
@@ -4097,3 +4116,11 @@ ca:
|
||||
errors:
|
||||
not_found:
|
||||
message_html: "<b>Si us plau torna-ho a provar</b> Això podria ser un problema temporal. Feu clic al botó Enrere per tornar a la pantalla anterior o torneu a <a href='/'>Inici</a> i torneu-ho a provar. <b>Contacteu amb suport</b><p> Si el problema persisteix o és urgent, si us plau, digueu-nos-ho. Esn pots enviar un correua a <a href='https://openfoodnetwork.org/ofn-local/' target='blank'>suport<p>katuma.org </a>.</p><p> Ens ajuda molt si pots donar tants detalls com sigui possible sobre de què estaves fent quan va aparèixer l'error.</p>"
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Cerca
|
||||
selector_with_filter:
|
||||
search_placeholder: Cerca
|
||||
pagination:
|
||||
next: Següent
|
||||
previous: Anterior
|
||||
|
||||
@@ -396,6 +396,25 @@ cy:
|
||||
destroy: "Dinistrio"
|
||||
rename: "Ail-enwi"
|
||||
admin:
|
||||
products_page:
|
||||
title: Cynnyrch
|
||||
filters:
|
||||
categories:
|
||||
title: Categorïau
|
||||
producers:
|
||||
title: Prynu bwyd
|
||||
colums: Colofnau
|
||||
columns:
|
||||
name: Enw
|
||||
unit: Uned
|
||||
price: Pris
|
||||
producer: Cynhyrchydd
|
||||
category: Categori
|
||||
columns_selector:
|
||||
unit: Uned
|
||||
price: Pris
|
||||
producer: Cynhyrchydd
|
||||
category: Categori
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "Nid yw'n bosib newid archeb a ganslwyd"
|
||||
begins_at: Yn dechrau am
|
||||
@@ -4281,3 +4300,11 @@ cy:
|
||||
title: "Mae'n ddrwg gennym, aeth rhywbeth o'i le (500)"
|
||||
unprocessable_entity:
|
||||
title: "Gwrthodwyd y newid roeddech chi am ei wneud (422)"
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Chwilio
|
||||
selector_with_filter:
|
||||
search_placeholder: Chwilio
|
||||
pagination:
|
||||
next: Nesaf
|
||||
previous: Blaenorol
|
||||
|
||||
@@ -394,6 +394,25 @@ de_CH:
|
||||
destroy: "Löschen"
|
||||
rename: "Umbenennen"
|
||||
admin:
|
||||
products_page:
|
||||
title: Produkte
|
||||
filters:
|
||||
categories:
|
||||
title: Lieferkategorien
|
||||
producers:
|
||||
title: Unsere Produzenten
|
||||
colums: Spalten
|
||||
columns:
|
||||
name: Name
|
||||
unit: Einheit
|
||||
price: Preis
|
||||
producer: Produzent
|
||||
category: Kategorie
|
||||
columns_selector:
|
||||
unit: Einheit
|
||||
price: Preis
|
||||
producer: Produzent
|
||||
category: Kategorie
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "Eine stornierte Bestellung kann nicht geändert werden."
|
||||
begins_at: Beginnt
|
||||
@@ -4158,3 +4177,11 @@ de_CH:
|
||||
x_years:
|
||||
one: "1 Jahr"
|
||||
other: "%{count} Jahren"
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Suche
|
||||
selector_with_filter:
|
||||
search_placeholder: Suche
|
||||
pagination:
|
||||
next: Nächste
|
||||
previous: Vorherige
|
||||
|
||||
@@ -399,6 +399,25 @@ de_DE:
|
||||
destroy: "Löschen"
|
||||
rename: "Umbenennen"
|
||||
admin:
|
||||
products_page:
|
||||
title: Produkte
|
||||
filters:
|
||||
categories:
|
||||
title: Lieferkategorien
|
||||
producers:
|
||||
title: Unsere Produzenten
|
||||
colums: Spalten
|
||||
columns:
|
||||
name: Name
|
||||
unit: Einheit
|
||||
price: Preis
|
||||
producer: Produzent
|
||||
category: Kategorie
|
||||
columns_selector:
|
||||
unit: Einheit
|
||||
price: Preis
|
||||
producer: Produzent
|
||||
category: Kategorie
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "Eine stornierte Bestellung kann nicht geändert werden."
|
||||
begins_at: Beginnt
|
||||
@@ -627,6 +646,7 @@ de_DE:
|
||||
conditional_blank: Darf nicht leer sein, wenn das Feld 'unit_type' leer ist.
|
||||
no_product: Es konnten keine passenden Produkte in der Datenbank gefunden werden.
|
||||
not_found: Konnte nicht in der Datenbank gefunden werden.
|
||||
category_not_found: stimmt nicht mit den verfügbaren Kategorien überein. Diese finden Sie auf der Seite zum Produktimport. Überprüfen Sie zusätzlich Ihre Angaben auf Tippfehler.
|
||||
not_updatable: Kann über den Produktimport nicht auf bestehende Produkte aktualisiert werden.
|
||||
blank: darf nicht leer sein
|
||||
products_no_permission: Sie sind nicht berechtigt, Produkte dieses Unternehmens zu verwalten.
|
||||
@@ -4244,3 +4264,10 @@ de_DE:
|
||||
components:
|
||||
multiple_checked_select:
|
||||
filter_placeholder: "Suche"
|
||||
search_input:
|
||||
placeholder: Suche
|
||||
selector_with_filter:
|
||||
search_placeholder: Suche
|
||||
pagination:
|
||||
next: Nächste
|
||||
previous: Vorherige
|
||||
|
||||
@@ -458,6 +458,28 @@ en:
|
||||
# Admin
|
||||
#
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
selected_categories: "%{count} categories selected"
|
||||
producers:
|
||||
title: Producers
|
||||
selected_producers: "%{count} producers selected"
|
||||
per_page: "%{count} items per page"
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "You can't change a cancelled order."
|
||||
# Common properties / models
|
||||
@@ -2810,6 +2832,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
report_header_customer_code: Customer Code
|
||||
report_header_product: Product
|
||||
report_header_product_properties: Product Properties
|
||||
report_header_product_tax_category: Product Tax Category
|
||||
report_header_quantity: Quantity
|
||||
report_header_max_quantity: Max Quantity
|
||||
report_header_variant: Variant
|
||||
@@ -2822,6 +2845,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
report_header_supplier: Supplier
|
||||
report_header_producer: Producer
|
||||
report_header_producer_suburb: Producer Suburb
|
||||
report_header_producer_charges_sales_tax?: GST/VAT Registered
|
||||
report_header_unit: Unit
|
||||
report_header_group_buy_unit_quantity: Group Buy Unit Quantity
|
||||
report_header_cost: Cost
|
||||
@@ -4439,3 +4463,11 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
components:
|
||||
multiple_checked_select:
|
||||
filter_placeholder: "Filter options"
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
selected_items: "%{count} selected"
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -295,6 +295,25 @@ en_AU:
|
||||
destroy: "Destroy"
|
||||
rename: "Rename"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
begins_at: Begins At
|
||||
begins_on: Begins On
|
||||
customer: Customer
|
||||
@@ -3735,3 +3754,11 @@ en_AU:
|
||||
spree/payment:
|
||||
one: Payment
|
||||
other: Payments
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -269,6 +269,25 @@ en_BE:
|
||||
delete: "Delete"
|
||||
add: "Add"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
begins_at: Begins At
|
||||
begins_on: Begins On
|
||||
customer: Customer
|
||||
@@ -3356,3 +3375,11 @@ en_BE:
|
||||
shop_name: "Shop Name"
|
||||
localized_number:
|
||||
invalid_format: has an invalid format. Please enter a number.
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -30,6 +30,7 @@ en_CA:
|
||||
shipping_category_id: "Shipping Category"
|
||||
variant_unit: "Variant Unit"
|
||||
variant_unit_name: "Variant Unit Name"
|
||||
unit_value: "Unit value"
|
||||
spree/credit_card:
|
||||
base: "Credit Card"
|
||||
number: "Number"
|
||||
@@ -398,6 +399,25 @@ en_CA:
|
||||
destroy: "Destroy"
|
||||
rename: "Rename"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "You can't change a cancelled order."
|
||||
begins_at: Begins At
|
||||
@@ -626,6 +646,7 @@ en_CA:
|
||||
conditional_blank: can't be blank if unit_type is blank
|
||||
no_product: did not match any products in the database
|
||||
not_found: not found in database
|
||||
category_not_found: doesn't match allowed categories. See the correct categories to choose from on the product import page, or check that there's no misspelling.
|
||||
not_updatable: cannot be updated on existing products via product import
|
||||
blank: can't be blank
|
||||
products_no_permission: you do not have permission to manage products for this enterprise
|
||||
@@ -882,7 +903,7 @@ en_CA:
|
||||
name: Name
|
||||
name_placeholder: eg. Professor Plum's Biodynamic Truffles
|
||||
groups: Groups
|
||||
groups_tip: Select any markets or groups that you are a member of. This will help customers find your enterprise.
|
||||
groups_tip: Select any markets, networks or groups that you are a member of. This will help customers find your enterprise.
|
||||
groups_placeholder: Start typing to search available markets or groups...
|
||||
primary_producer: Primary Producer?
|
||||
primary_producer_tip: Select 'Producer' if you are a primary producer, grower, maker or designer
|
||||
@@ -1579,7 +1600,7 @@ en_CA:
|
||||
menu_2_url: "/map"
|
||||
menu_3_title: "Producers"
|
||||
menu_3_url: "/producers"
|
||||
menu_4_title: "Markets"
|
||||
menu_4_title: "Networks"
|
||||
menu_4_url: "/groups"
|
||||
menu_5_title: "About"
|
||||
menu_5_url: "https://about.openfoodnetwork.ca"
|
||||
@@ -1641,7 +1662,7 @@ en_CA:
|
||||
label_map: "Map"
|
||||
label_producer: "Producer"
|
||||
label_producers: "Producers"
|
||||
label_groups: "Markets"
|
||||
label_groups: "Networks"
|
||||
label_about: "About"
|
||||
label_blog: "Blog"
|
||||
label_support: "Support"
|
||||
@@ -1980,7 +2001,7 @@ en_CA:
|
||||
shopping_tabs_about: "About"
|
||||
shopping_tabs_producers: "Producers"
|
||||
shopping_tabs_contact: "Contact"
|
||||
shopping_tabs_groups: "Markets"
|
||||
shopping_tabs_groups: "Networks"
|
||||
shopping_contact_address: "Address"
|
||||
shopping_contact_web: "Contact"
|
||||
shopping_contact_social: "Follow"
|
||||
@@ -2045,14 +2066,14 @@ en_CA:
|
||||
components_profiles_show: "Show profiles"
|
||||
components_filters_nofilters: "No filters"
|
||||
components_filters_clearfilters: "Clear all filters"
|
||||
groups_title: Markets
|
||||
groups_headline: 'Markets / Regions '
|
||||
groups_text: "Markets are collectives of producers, hubs and distributors who share something in common like location, farmers market or philosophy. This makes your shopping experience easier. So explore our groups and have the curating done for you."
|
||||
groups_title: Networks
|
||||
groups_headline: 'Networks / Regions '
|
||||
groups_text: "Networks are collectives of producers, hubs and distributors who share something in common like location, farmers market or philosophy. This makes your shopping experience easier. So explore our networks and have the curating done for you."
|
||||
groups_search: "Search name or keyword"
|
||||
groups_no_groups: "No groups found"
|
||||
groups_about: "About Us"
|
||||
groups_producers: "Our vendors"
|
||||
groups_hubs: "Go Shopping!"
|
||||
groups_producers: "Members"
|
||||
groups_hubs: "Go Shopping"
|
||||
groups_contact_web: Contact
|
||||
groups_contact_social: Follow
|
||||
groups_contact_address: Address
|
||||
@@ -4234,3 +4255,10 @@ en_CA:
|
||||
components:
|
||||
multiple_checked_select:
|
||||
filter_placeholder: "Filter options"
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -274,6 +274,25 @@ en_DE:
|
||||
delete: "Delete"
|
||||
add: "Add"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
begins_at: Begins At
|
||||
begins_on: Begins On
|
||||
customer: Customer
|
||||
@@ -3372,3 +3391,11 @@ en_DE:
|
||||
shop_name: "Shop Name"
|
||||
localized_number:
|
||||
invalid_format: has an invalid format. Please enter a number.
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -399,6 +399,25 @@ en_FR:
|
||||
destroy: "Destroy"
|
||||
rename: "Rename"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "You can't change a cancelled order."
|
||||
begins_at: Begins At
|
||||
@@ -627,6 +646,7 @@ en_FR:
|
||||
conditional_blank: can't be blank if unit_type is blank
|
||||
no_product: did not match any products in the database
|
||||
not_found: not found in database
|
||||
category_not_found: doesn't match allowed categories. See the correct categories to choose from on the product import page, or check that there's no misspelling.
|
||||
not_updatable: cannot be updated on existing products via product import
|
||||
blank: can't be blank
|
||||
products_no_permission: you do not have permission to manage products for this enterprise
|
||||
@@ -4236,3 +4256,10 @@ en_FR:
|
||||
components:
|
||||
multiple_checked_select:
|
||||
filter_placeholder: "Filter options"
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -396,6 +396,25 @@ en_GB:
|
||||
destroy: "Destroy"
|
||||
rename: "Rename"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "You can't change a cancelled order."
|
||||
begins_at: Begins At
|
||||
@@ -4213,3 +4232,11 @@ en_GB:
|
||||
unprocessable_entity:
|
||||
title: "The change you wanted was rejected (422)"
|
||||
message_html: "<p>The change you wanted was rejected. Maybe you tried to change something you don't have access to. <br><h3><a href='/' >Return home</a></h3> </p>"
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -396,6 +396,25 @@ en_IE:
|
||||
destroy: "Destroy"
|
||||
rename: "Rename"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "You can't change a cancelled order."
|
||||
begins_at: Begins At
|
||||
@@ -4208,3 +4227,11 @@ en_IE:
|
||||
unprocessable_entity:
|
||||
title: "The change you wanted was rejected (422)"
|
||||
message_html: "<p>The change you wanted was rejected. Maybe you tried to change something you don't have access to. <br><h3><a href='/' >Return home</a></h3> </p>"
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -282,6 +282,25 @@ en_IN:
|
||||
delete: "Delete"
|
||||
add: "Add"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
begins_at: Begins At
|
||||
begins_on: Begins On
|
||||
customer: Customer
|
||||
@@ -3631,3 +3650,11 @@ en_IN:
|
||||
shipment:
|
||||
cannot_ready: "Cannot ready shipment."
|
||||
invalid_taxonomy_id: "Invalid taxonomy id."
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -395,6 +395,25 @@ en_NZ:
|
||||
destroy: "Destroy"
|
||||
rename: "Rename"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "You can't change a cancelled order."
|
||||
begins_at: Begins At
|
||||
@@ -4032,3 +4051,11 @@ en_NZ:
|
||||
spree/payment:
|
||||
one: Payment
|
||||
other: Payments
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -281,6 +281,25 @@ en_PH:
|
||||
delete: "Delete"
|
||||
add: "Add"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
begins_at: Begins At
|
||||
begins_on: Begins On
|
||||
customer: Customer
|
||||
@@ -3559,3 +3578,11 @@ en_PH:
|
||||
shipment:
|
||||
cannot_ready: "Cannot ready shipment."
|
||||
invalid_taxonomy_id: "Invalid category ID."
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
@@ -395,6 +395,25 @@ en_US:
|
||||
destroy: "Destroy"
|
||||
rename: "Rename"
|
||||
admin:
|
||||
products_page:
|
||||
title: Products
|
||||
filters:
|
||||
categories:
|
||||
title: Categories
|
||||
producers:
|
||||
title: Producers
|
||||
colums: Columns
|
||||
columns:
|
||||
name: Name
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
columns_selector:
|
||||
unit: Unit
|
||||
price: Price
|
||||
producer: Producer
|
||||
category: Category
|
||||
adjustments:
|
||||
skipped_changing_canceled_order: "You can't change a cancelled order."
|
||||
begins_at: Begins At
|
||||
@@ -1530,9 +1549,9 @@ en_US:
|
||||
menu_4_url: "/groups"
|
||||
menu_5_title: "About"
|
||||
menu_5_url: "https://about.openfoodnetwork.net/"
|
||||
menu_6_title: "Donate"
|
||||
menu_6_url: "https://donate.openfoodnetwork.net"
|
||||
menu_7_title: "Support"
|
||||
menu_6_title: "Pricing"
|
||||
menu_6_url: "https://about.openfoodnetwork.net/?page_id=114"
|
||||
menu_7_title: "Help"
|
||||
menu_7_url: "https://about.openfoodnetwork.net/?page_id=1123"
|
||||
logo: "Logo (640x130)"
|
||||
logo_mobile: "Mobile logo (75x26)"
|
||||
@@ -4118,3 +4137,11 @@ en_US:
|
||||
x_years:
|
||||
one: "1 year"
|
||||
other: "%{count} years"
|
||||
components:
|
||||
search_input:
|
||||
placeholder: Search
|
||||
selector_with_filter:
|
||||
search_placeholder: Search
|
||||
pagination:
|
||||
next: Next
|
||||
previous: Previous
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user