mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
Compare commits
1 Commits
v4.4.26
...
10228-spli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8f88ba687 |
@@ -2,4 +2,3 @@
|
||||
.gitignore
|
||||
log/*
|
||||
tmp/*
|
||||
node_modules/
|
||||
|
||||
8
.env
8
.env
@@ -52,8 +52,6 @@ SMTP_PASSWORD="f00d"
|
||||
|
||||
# see="https://developers.google.com/maps/documentation/javascript/get-api-key
|
||||
# GOOGLE_MAPS_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
# see https://developers.google.com/maps/documentation/javascript/localization#Region
|
||||
# GOOGLE_MAPS_REGION="XX"
|
||||
|
||||
# Stripe details for instance account
|
||||
# Find these under 'Developers' -> 'API keys' in your Stripe account dashboard.
|
||||
@@ -63,9 +61,3 @@ SMTP_PASSWORD="f00d"
|
||||
# STRIPE_INSTANCE_PUBLISHABLE_KEY="pk_test_xxxx" # This can be a test key or a live key
|
||||
# STRIPE_CLIENT_ID="ca_xxxx" # This can be a development ID or a production ID
|
||||
# STRIPE_ENDPOINT_SECRET="whsec_xxxx"
|
||||
|
||||
# New relic settings
|
||||
# see: https://one.eu.newrelic.com/admin-portal/, Administration > API keys to get the license key
|
||||
# NEW_RELIC_AGENT_ENABLED=true
|
||||
# NEW_RELIC_APP_NAME="Open Food Network"
|
||||
# NEW_RELIC_LICENSE_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
@@ -5,17 +5,9 @@
|
||||
#
|
||||
# cp .env.development .env.local
|
||||
|
||||
VERBOSE_QUERY_LOGS=true
|
||||
|
||||
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
OFN_REDIS_URL="redis://localhost:6379/1"
|
||||
OFN_REDIS_JOBS_URL="redis://localhost:6379/2"
|
||||
|
||||
SITE_URL="0.0.0.0:3000"
|
||||
|
||||
# Deactivate rack-timeout in development.
|
||||
# https://github.com/zombocom/rack-timeout#configuring
|
||||
RACK_TIMEOUT_SERVICE_TIMEOUT="0"
|
||||
RACK_TIMEOUT_WAIT_TIMEOUT="0"
|
||||
RACK_TIMEOUT_WAIT_OVERTIME="0"
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
STRIPE_SECRET_TEST_API_KEY="bogus_key"
|
||||
STRIPE_CUSTOMER="bogus_customer"
|
||||
STRIPE_ACCOUNT="bogus_account"
|
||||
STRIPE_CLIENT_ID="bogus_client_id"
|
||||
STRIPE_PUBLIC_TEST_API_KEY="bogus_stripe_publishable_key"
|
||||
|
||||
SITE_URL="test.host"
|
||||
|
||||
|
||||
11
.gitattributes
vendored
11
.gitattributes
vendored
@@ -1,11 +0,0 @@
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
* text=auto
|
||||
|
||||
# Set line endings to LF, even on Windows. Otherwise, execution within Docker fails.
|
||||
# See https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings#per-repository-settings
|
||||
*.sh text eol=lf
|
||||
|
||||
# Same thing for following files, but they don't have an sh extension
|
||||
pre-commit eol=lf
|
||||
webpack-dev-server eol=lf
|
||||
install-bundler eol=lf
|
||||
28
.github/ISSUE_TEMPLATE/release.md
vendored
28
.github/ISSUE_TEMPLATE/release.md
vendored
@@ -7,34 +7,30 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## 1. Preparation on Thursday
|
||||
## Preparation on Thursday
|
||||
|
||||
- [ ] Merge pull requests in the [Ready To Go] column
|
||||
- [ ] Include translations: `script/release/update_locales`
|
||||
- [ ] Increment version number: `git push upstream HEAD:refs/tags/vX.Y.Z`
|
||||
- Major: if server changes are required (eg. provision with ofn-install)
|
||||
- Minor: larger change that is irreversible (eg. migration deleting data)
|
||||
- Patch: all others. Shortcut: `script/release/tag`
|
||||
- [ ] Include translations: `tx pull --force`
|
||||
- [ ] [Draft new release]. Look at previous [releases] for inspiration.
|
||||
- Select new release tag
|
||||
- _Generate release notes_ and check to ensure all items are arranged in the right category.
|
||||
- [ ] Notify [#instance-managers] of user-facing :eyes:, API :warning: and experimental :construction: changes.
|
||||
- [ ] Notify [#instance-managers] of user-facing changes.
|
||||
|
||||
## 2. Testing
|
||||
## Testing
|
||||
|
||||
- [ ] [Find build] of the release commit and copy it below.
|
||||
- [ ] Move this issue to Test Ready.
|
||||
- [ ] Notify `@testers` in [#testing].
|
||||
- [ ] Test build: [Deploy to Staging] with release tag.
|
||||
- [ ] Test build: <!-- paste build link here, e.g. https://semaphore...builds/1234 -->
|
||||
|
||||
## 3. Finish on Tuesday
|
||||
## Finish on Tuesday
|
||||
|
||||
- [ ] Publish and notify [#global-community] (this is automatically posted with a plugin)
|
||||
- [ ] Publish and notify [#global-community]:
|
||||
> The next release is ready: https://github.com/openfoodfoundation/openfoodnetwork/releases/latest
|
||||
- [ ] Deploy the new release to all managed instances.
|
||||
<details><summary>Command line instructions</summary>
|
||||
<pre>
|
||||
cd ofn-install
|
||||
git pull
|
||||
ansible-playbook --limit all_prod --extra-vars "git_version=vX.Y.Z" playbooks/deploy.yml
|
||||
ansible-playbook --limit all-prod --extra-vars "git_version=vx.y.z" playbooks/deploy.yml
|
||||
</pre>
|
||||
</details>
|
||||
- [ ] Notify [#instance-managers]:
|
||||
@@ -45,9 +41,9 @@ The full process is described at https://github.com/openfoodfoundation/openfoodn
|
||||
|
||||
[Ready To Go]: #zenhub
|
||||
[Transifex pull request]: https://github.com/openfoodfoundation/openfoodnetwork/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+head%3Atransifex
|
||||
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A
|
||||
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A%23%23+User+facing+changes+:eyes:%0A%0A%0A%0A%23%23+Technical+changes+:wrench:%0A%0A
|
||||
[releases]: https://github.com/openfoodfoundation/openfoodnetwork/releases
|
||||
[#instance-managers]: https://app.slack.com/client/T02G54U79/CG7NJ966B
|
||||
[#testing]: https://openfoodnetwork.slack.com/app_redirect?channel=C02TZ6X00
|
||||
[Deploy to Staging]: https://github.com/openfoodfoundation/openfoodnetwork/actions/workflows/stage.yml
|
||||
[Find build]: https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2/branches/master
|
||||
[#global-community]: https://app.slack.com/client/T02G54U79/C59ADD8F2
|
||||
|
||||
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -20,12 +20,7 @@
|
||||
|
||||
<!-- Please select one for your PR and delete the other. -->
|
||||
|
||||
Changelog Category (reviewers may add a label for the release notes):
|
||||
|
||||
- [x] User facing changes
|
||||
- [ ] API changes (V0, V1, DFC or Webhook)
|
||||
- [ ] Technical changes only
|
||||
- [ ] Feature toggled
|
||||
Changelog Category: User facing changes | Technical changes
|
||||
|
||||
<!-- Choose a pull request title above which explains your change to a
|
||||
a user of the Open Food Network app. -->
|
||||
|
||||
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -6,12 +6,10 @@ updates:
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 10
|
||||
# Only specific requirements are specified in Gemfile, so don't touch it.
|
||||
versioning-strategy: lockfile-only
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
# All versions are specified in package.json, so please update them.
|
||||
versioning-strategy: increase
|
||||
versioning-strategy: lockfile-only
|
||||
|
||||
30
.github/release.yml
vendored
30
.github/release.yml
vendored
@@ -1,30 +0,0 @@
|
||||
changelog:
|
||||
# Categorise according to what an instance manager needs to know
|
||||
categories:
|
||||
# Posted in advance for #instance-managers
|
||||
- title: "User-facing changes 👀"
|
||||
labels:
|
||||
- '*'
|
||||
exclude:
|
||||
labels:
|
||||
- api changes
|
||||
- dependencies
|
||||
- feature toggled
|
||||
- technical changes only
|
||||
|
||||
- title: "API changes ⚠️"
|
||||
labels:
|
||||
- api changes
|
||||
|
||||
- title: "Experimental features for testing 🚧" # may be tested by instance managers
|
||||
labels:
|
||||
- feature toggled
|
||||
|
||||
# Instance managers ignore below
|
||||
- title: "Technical changes 🛠️"
|
||||
labels:
|
||||
- technical changes only
|
||||
|
||||
- title: "Dependencies 📦"
|
||||
labels:
|
||||
- dependencies
|
||||
3
.github/workflows/brakeman-analysis.yml
vendored
3
.github/workflows/brakeman-analysis.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
- name: Setup Brakeman
|
||||
env:
|
||||
BRAKEMAN_VERSION: '5.4.0'
|
||||
BRAKEMAN_VERSION: '4.10' # SARIF support is provided in Brakeman version 4.10+
|
||||
run: |
|
||||
gem install brakeman --version $BRAKEMAN_VERSION
|
||||
|
||||
@@ -41,7 +41,6 @@ jobs:
|
||||
- name: Scan
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
|
||||
brakeman -f sarif -o output.sarif.json .
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
|
||||
205
.github/workflows/build.yml
vendored
205
.github/workflows/build.yml
vendored
@@ -17,8 +17,8 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
controllers:
|
||||
runs-on: ubuntu-22.04
|
||||
knapsack_rspec_controllers:
|
||||
runs-on: ubuntu-20.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
node-version: 16
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
@@ -68,6 +68,7 @@ jobs:
|
||||
bundle exec rake db:schema:load
|
||||
|
||||
- name: Run tests
|
||||
|
||||
env:
|
||||
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: 864ef557d85ea8e603e086c0387d5154
|
||||
KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
|
||||
@@ -81,12 +82,12 @@ jobs:
|
||||
# https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it
|
||||
#KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES: true
|
||||
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/controllers/**/*_spec.rb}"
|
||||
|
||||
run: |
|
||||
git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
|
||||
models:
|
||||
runs-on: ubuntu-22.04
|
||||
knapsack_rspec_models:
|
||||
runs-on: ubuntu-20.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10
|
||||
@@ -106,10 +107,10 @@ jobs:
|
||||
# [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]
|
||||
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]
|
||||
ci_node_index: [0, 1, 2, 3, 4, 5, 6]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -125,7 +126,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
node-version: 16
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
@@ -154,8 +155,8 @@ jobs:
|
||||
run: |
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
|
||||
system_admin:
|
||||
runs-on: ubuntu-22.04
|
||||
knapsack_rspec_system_admin:
|
||||
runs-on: ubuntu-20.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10
|
||||
@@ -175,10 +176,10 @@ jobs:
|
||||
# [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: [13]
|
||||
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, 10, 11, 12]
|
||||
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -194,7 +195,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
node-version: 16
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
@@ -232,8 +233,8 @@ jobs:
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
system_consumer:
|
||||
runs-on: ubuntu-22.04
|
||||
knapsack_rspec_system_consumer:
|
||||
runs-on: ubuntu-20.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10
|
||||
@@ -253,10 +254,10 @@ jobs:
|
||||
# [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: [12]
|
||||
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, 10, 11]
|
||||
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -272,7 +273,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
node-version: 16
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
@@ -310,86 +311,8 @@ jobs:
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
engines:
|
||||
runs-on: ubuntu-22.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: [2]
|
||||
# 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]
|
||||
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-file: .node-version
|
||||
|
||||
- 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
|
||||
|
||||
test_the_rest:
|
||||
runs-on: ubuntu-22.04
|
||||
knapsack_rspec_engines:
|
||||
runs-on: ubuntu-20.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10
|
||||
@@ -428,7 +351,85 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
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
|
||||
@@ -459,7 +460,7 @@ jobs:
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
|
||||
non_knapsack_jest_karma:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10
|
||||
@@ -488,7 +489,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
node-version: 16
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
14
.github/workflows/linters.yml
vendored
14
.github/workflows/linters.yml
vendored
@@ -9,11 +9,7 @@ jobs:
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- uses: ruby/setup-ruby@v1
|
||||
|
||||
- run: git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
|
||||
|
||||
- name: rubocop
|
||||
uses: reviewdog/action-rubocop@v2
|
||||
with:
|
||||
@@ -28,16 +24,6 @@ jobs:
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- run: git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
|
||||
|
||||
- name: prettier
|
||||
uses: EPMatt/reviewdog-action-prettier@v1
|
||||
with:
|
||||
|
||||
2
.github/workflows/mapi.yml
vendored
2
.github/workflows/mapi.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
with:
|
||||
mapi-token: ${{ secrets.MAPI_TOKEN }}
|
||||
api-url: http://localhost:3000
|
||||
api-spec: swagger/v1.yaml
|
||||
api-spec: swagger/v1/swagger.yaml
|
||||
target: openfoodfoundation/openfoodnetwork
|
||||
duration: 1min
|
||||
sarif-report: mapi.sarif
|
||||
|
||||
66
.github/workflows/stage.yml
vendored
66
.github/workflows/stage.yml
vendored
@@ -1,66 +0,0 @@
|
||||
name: "Deploy to Staging"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
server:
|
||||
description: "Staging Server"
|
||||
type: choice
|
||||
required: true
|
||||
options:
|
||||
- staging.openfoodnetwork.org.uk
|
||||
- staging.openfoodnetwork.org.au
|
||||
- staging.coopcircuits.fr
|
||||
commit_ref:
|
||||
description: "Commit Reference"
|
||||
type: string
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
deploy_pr:
|
||||
if: contains(fromJSON('["pr-staged-uk", "pr-staged-au", "pr-staged-fr"]'), github.event.label.name)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Check user has write access"
|
||||
uses: "lannonbr/repo-permission-check-action@2.0.2"
|
||||
with:
|
||||
permission: "write"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Configure deployment key
|
||||
if: success()
|
||||
run: |
|
||||
install -m 600 -D /dev/null ~/.ssh/id_rsa
|
||||
echo "${{ secrets.DEPLOYMENT_KEY }}" > ~/.ssh/id_rsa
|
||||
echo "${{ secrets.DEPLOYMENT_HOSTS }}" > ~/.ssh/known_hosts
|
||||
|
||||
- name: Deploy to Staging
|
||||
if: success()
|
||||
run: |
|
||||
ssh ofn-deploy@${{ github.event.label.description }} -o LogLevel=ERROR "pull-request-${{ github.event.pull_request.number }} ."
|
||||
|
||||
deploy_branch:
|
||||
if: ${{ inputs.server }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Check user has write access"
|
||||
uses: "lannonbr/repo-permission-check-action@2.0.2"
|
||||
with:
|
||||
permission: "write"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Configure deployment key
|
||||
if: success()
|
||||
run: |
|
||||
install -m 600 -D /dev/null ~/.ssh/id_rsa
|
||||
echo "${{ secrets.DEPLOYMENT_KEY }}" > ~/.ssh/id_rsa
|
||||
echo "${{ secrets.DEPLOYMENT_HOSTS }}" > ~/.ssh/known_hosts
|
||||
|
||||
- name: Deploy to Staging
|
||||
if: success()
|
||||
run: |
|
||||
ssh ofn-deploy@${{ inputs.server }} -o LogLevel=ERROR "$GITHUB_REF_NAME ${{ inputs.commit_ref || github.sha }}"
|
||||
@@ -1 +1 @@
|
||||
17.9.1
|
||||
14.16.1
|
||||
|
||||
@@ -1,27 +1,17 @@
|
||||
# Ignore a lot of things, but we should enable where it can be helpful.
|
||||
|
||||
# Basically, ignore everythings expect app/webpacker/controllers/*.js and app/webpacker/packs/*.js
|
||||
*.css
|
||||
*.scss
|
||||
# Except v2
|
||||
!/app/webpacker/css/admin/v2/**/*.scss
|
||||
*.md
|
||||
*.yml
|
||||
*.yaml
|
||||
*.json
|
||||
*.html
|
||||
|
||||
# JS
|
||||
# Enabled: app/webpacker/controllers/*.js and app/webpacker/packs/*.js
|
||||
babel.config.js
|
||||
postcss.config.js
|
||||
|
||||
# SCSS
|
||||
# Enabled: most of admin
|
||||
/app/webpacker/css/admin/globals/mixins.scss
|
||||
/app/webpacker/css/admin/globals/variables.scss
|
||||
/app/webpacker/css/admin/shared/
|
||||
/app/webpacker/css/admin_v3/globals/variables.scss
|
||||
/app/webpacker/css/darkswarm/
|
||||
/app/webpacker/css/mail/
|
||||
/app/webpacker/css/shared/
|
||||
|
||||
# More
|
||||
/app/assets/
|
||||
/config/
|
||||
/coverage/
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
{
|
||||
"printWidth": 100
|
||||
}
|
||||
{}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
#
|
||||
# These are the rules we agreed upon and we work towards.
|
||||
AllCops:
|
||||
NewCops: enable
|
||||
NewCops: disable
|
||||
SuggestExtensions: false
|
||||
TargetRailsVersion: 5.0
|
||||
Exclude:
|
||||
- 'bin/**/*'
|
||||
- 'db/**/*'
|
||||
@@ -25,7 +26,6 @@ Metrics/BlockLength:
|
||||
AllowedMethods: [
|
||||
"class_eval",
|
||||
"collection",
|
||||
"configure",
|
||||
"context",
|
||||
"delete",
|
||||
"describe",
|
||||
@@ -39,28 +39,16 @@ Metrics/BlockLength:
|
||||
"put",
|
||||
"resource",
|
||||
"resources",
|
||||
"response",
|
||||
"scenario",
|
||||
"shared_examples",
|
||||
"shared_examples_for",
|
||||
"xdescribe",
|
||||
]
|
||||
|
||||
Metrics/ParameterLists:
|
||||
CountKeywordArgs: false
|
||||
|
||||
Rails/ApplicationRecord:
|
||||
Exclude:
|
||||
# Migrations should not contain application code:
|
||||
- "db/migrate/*.rb"
|
||||
|
||||
# Allow many-to-many associations without explicit model.
|
||||
# - It avoids the additional code of a model class.
|
||||
# - It simplifies the declaration of the association.
|
||||
# - Rails may know that there are no callbacks associated.
|
||||
Rails/HasAndBelongsToMany:
|
||||
Enabled: false
|
||||
|
||||
Rails/SkipsModelValidations:
|
||||
AllowedMethods:
|
||||
- "touch"
|
||||
@@ -70,10 +58,6 @@ Rails/SkipsModelValidations:
|
||||
- "update_column"
|
||||
- "update_columns"
|
||||
|
||||
Rails/OutputSafety:
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
@@ -105,8 +89,6 @@ Naming/VariableNumber:
|
||||
AllowedIdentifiers:
|
||||
- street_address_1
|
||||
- street_address_2
|
||||
AllowedPatterns:
|
||||
- _v[\d]+
|
||||
|
||||
Bundler/DuplicatedGem:
|
||||
Enabled: false
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
3.1.4
|
||||
3.0.3
|
||||
|
||||
@@ -57,7 +57,6 @@ TL;DR:
|
||||
* Maintain a clean commit history
|
||||
* Use a style consistent with the rest of the codebase
|
||||
* Before submitting, [rebase your work][rebase] on the current master branch
|
||||
* After submitting, be sure to check the [CI test results](ci). Click on a ❌ result to view the logged results and investigate.
|
||||
|
||||
From here, your pull request will progress through the [Review, Test, Merge & Deploy process][process].
|
||||
|
||||
@@ -71,5 +70,4 @@ From here, your pull request will progress through the [Review, Test, Merge & De
|
||||
[slack-dev]: https://openfoodnetwork.slack.com/messages/C2GQ45KNU
|
||||
[ofn-transifex]: https://www.transifex.com/open-food-foundation/open-food-network/
|
||||
[i18n]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Internationalisation-%28i18n%29
|
||||
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/5
|
||||
[ci]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Continuous-Integration
|
||||
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/2
|
||||
|
||||
25
Dockerfile
25
Dockerfile
@@ -28,18 +28,14 @@ RUN apt-get update && apt-get install -y \
|
||||
gnupg
|
||||
|
||||
# Setup ENV variables
|
||||
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:/usr/local/src/nodenv/shims:/usr/local/src/nodenv/bin:$PATH
|
||||
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:$PATH
|
||||
ENV RBENV_ROOT /usr/local/src/rbenv
|
||||
ENV NODENV_ROOT /usr/local/src/nodenv
|
||||
ENV CONFIGURE_OPTS --disable-install-doc
|
||||
ENV BUNDLE_PATH /bundles
|
||||
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# trim spaces and line return from .ruby-version file
|
||||
COPY .ruby-version .ruby-version.raw
|
||||
RUN cat .ruby-version.raw | tr -d '\r\t ' > .ruby-version
|
||||
COPY .ruby-version .
|
||||
|
||||
# Install Rbenv & Ruby
|
||||
RUN git clone --depth 1 https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
|
||||
@@ -54,19 +50,10 @@ RUN sh -c "echo 'deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-
|
||||
apt-get update && \
|
||||
apt-get install -yqq --no-install-recommends postgresql-client-10 libpq-dev
|
||||
|
||||
|
||||
# trim spaces and line return from .node-version file
|
||||
COPY .node-version .node-version.raw
|
||||
RUN cat .node-version.raw | tr -d '\r\t ' > .node-version
|
||||
|
||||
# Install Node and Yarn with Nodenv
|
||||
RUN git clone --depth 1 https://github.com/nodenv/nodenv.git ${NODENV_ROOT} && \
|
||||
git clone --depth 1 https://github.com/nodenv/node-build.git ${NODENV_ROOT}/plugins/node-build && \
|
||||
git clone --depth 1 https://github.com/pine/nodenv-yarn-install.git ${NODENV_ROOT}/plugins/nodenv-yarn-install && \
|
||||
git clone --depth 1 https://github.com/nodenv/nodenv-package-rehash.git ${NODENV_ROOT}/plugins/nodenv-package-rehash && \
|
||||
echo 'eval "$(nodenv init -)"' >> /etc/profile.d/nodenv.sh && \
|
||||
nodenv install $(cat .node-version) && \
|
||||
nodenv global $(cat .node-version)
|
||||
# Install NodeJs and yarn
|
||||
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - \
|
||||
&& apt-get install --no-install-recommends -y nodejs \
|
||||
&& npm install -g yarn
|
||||
|
||||
# Install Chrome
|
||||
RUN wget --quiet -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
|
||||
|
||||
@@ -11,9 +11,6 @@ Head to our wiki on [Learning Rails](https://github.com/openfoodfoundation/openf
|
||||
The fastest way to make it work locally is to use Docker, you only need to setup git, see the [Docker setup guide](docker/README.md).
|
||||
Otherwise, for a local setup you will need:
|
||||
* Ruby and bundler (check current Ruby version in [.ruby-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.ruby-version) file)
|
||||
- To manage versions, it's recommended to use [rbenv](https://github.com/rbenv/rbenv) or [RVM](https://rvm.io/)
|
||||
* Node and yarn (check current Node version in [.node-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.node-version) file)
|
||||
- [nodevn](https://github.com/nodenv/nodenv) is recommended.
|
||||
* PostgreSQL database
|
||||
* Redis (for background jobs)
|
||||
* Chrome (for testing)
|
||||
@@ -23,13 +20,15 @@ The following guides will provide OS-specific step-by-step instructions to get t
|
||||
- [Debian Setup Guide][debian]
|
||||
- [OSX Setup Guide][osx]
|
||||
|
||||
If you are likely to need to manage multiple version of ruby on your local machine, we recommend version managers such as [rbenv](https://github.com/rbenv/rbenv) or [RVM](https://rvm.io/).
|
||||
|
||||
For those new to Rails, the following tutorial will help get you up to speed with configuring a [Rails environment](http://guides.rubyonrails.org/getting_started.html).
|
||||
|
||||
### Get it
|
||||
|
||||
So you have set up your local environment according to the requirements listed above. If you're planning on contributing code to the project (which we [LOVE](CONTRIBUTING.md)), it is a good idea to begin by forking this repo using the `Fork` button in the top-right corner of this screen. You should then be able to use `git clone` to copy your fork onto your local machine:
|
||||
|
||||
git clone git@github.com:YOUR_GITHUB_USERNAME_HERE/openfoodnetwork.git
|
||||
git clone https://github.com/YOUR_GITHUB_USERNAME_HERE/openfoodnetwork
|
||||
|
||||
Jump into your new local copy of the Open Food Network:
|
||||
|
||||
@@ -37,7 +36,7 @@ Jump into your new local copy of the Open Food Network:
|
||||
|
||||
And then add an `upstream` remote that points to the main repo:
|
||||
|
||||
git remote add upstream git@github.com:openfoodfoundation/openfoodnetwork.git
|
||||
git remote add upstream https://github.com/openfoodfoundation/openfoodnetwork
|
||||
|
||||
Fetch the latest version of `master` from `upstream` (ie. the main repo):
|
||||
|
||||
@@ -48,14 +47,14 @@ Fetch the latest version of `master` from `upstream` (ie. the main repo):
|
||||
First, you need to create the database user the app will use by manually typing the following in your terminal:
|
||||
|
||||
```sh
|
||||
sudo --login --user=postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"
|
||||
$ sudo -u postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"
|
||||
```
|
||||
|
||||
This will create the "ofn" user as superuser and allowing it to create databases. If this command fails, check the [troubleshooting section](#creating-the-database) for an alternative.
|
||||
|
||||
Next, it is _strongly recommended_ to run the setup script.
|
||||
```sh
|
||||
./script/setup
|
||||
$ script/setup
|
||||
```
|
||||
If the script succeeds you're ready to start developing. If not, take a look at the output as it should be informative enough to help you troubleshoot.
|
||||
|
||||
@@ -114,13 +113,13 @@ Below are fixes to potential issues that can happen during the installation proc
|
||||
|
||||
#### Creating the database
|
||||
|
||||
If the `sudo -u postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"` command doesn't work, you can run the following commands instead:
|
||||
If the ```$ sudo -u postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PASSWORD 'f00d'"``` command doesn't work, you can run the following commands instead:
|
||||
```
|
||||
createuser --superuser --pwprompt ofn
|
||||
# Enter password for new role: f00d
|
||||
# Enter it again: f00d
|
||||
createdb open_food_network_dev --owner=ofn
|
||||
createdb open_food_network_test --owner=ofn
|
||||
$ createuser --superuser --pwprompt ofn
|
||||
Enter password for new role: f00d
|
||||
Enter it again: f00d
|
||||
$ createdb open_food_network_dev --owner=ofn
|
||||
$ createdb open_food_network_test --owner=ofn
|
||||
```
|
||||
If these commands succeed, you should be able to [continue the setup process](#get-it-running).
|
||||
|
||||
|
||||
55
Gemfile
55
Gemfile
@@ -1,13 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
ruby "3.0.3"
|
||||
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
|
||||
|
||||
ruby File.read('.ruby-version').chomp
|
||||
|
||||
gem 'dotenv-rails', require: 'dotenv/rails-now' # Load ENV vars before other gems
|
||||
|
||||
gem 'rails'
|
||||
gem 'rails', '>= 6.1.4'
|
||||
|
||||
# Active Storage
|
||||
gem "active_storage_validations"
|
||||
@@ -15,11 +14,11 @@ gem "aws-sdk-s3", require: false
|
||||
gem "image_processing"
|
||||
|
||||
gem 'activemerchant', '>= 1.78.0'
|
||||
gem 'rexml'
|
||||
gem 'angular-rails-templates', '>= 0.3.0'
|
||||
gem 'awesome_nested_set'
|
||||
gem 'ransack', '~> 4.1.0'
|
||||
gem 'ransack', '2.4.2'
|
||||
gem 'responders'
|
||||
gem 'rexml'
|
||||
gem 'webpacker', '~> 5'
|
||||
|
||||
gem 'i18n'
|
||||
@@ -28,11 +27,10 @@ gem 'rails-i18n'
|
||||
gem 'rails_safe_tasks', '~> 1.0'
|
||||
|
||||
gem "activerecord-import"
|
||||
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-7"
|
||||
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-6"
|
||||
gem "fog-aws", "~> 2.0" # db2fog does not support v3
|
||||
gem "mime-types" # required by fog
|
||||
|
||||
gem "validates_lengths_from_database"
|
||||
gem "valid_email2"
|
||||
|
||||
gem "catalog", path: "./engines/catalog"
|
||||
@@ -65,16 +63,15 @@ gem 'devise-token_authenticatable'
|
||||
gem 'jwt', '~> 2.3'
|
||||
gem 'oauth2', '~> 1.4.7' # Used for Stripe Connect
|
||||
|
||||
gem 'datafoodconsortium-connector'
|
||||
gem 'jsonapi-serializer'
|
||||
gem 'pagy', '~> 5.1'
|
||||
|
||||
gem 'rswag-api'
|
||||
gem 'rswag-ui'
|
||||
|
||||
gem 'omniauth_openid_connect'
|
||||
gem 'omniauth-rails_csrf_protection'
|
||||
gem 'gitlab-omniauth-openid-connect', require: 'omniauth_openid_connect'
|
||||
gem 'openid_connect', '~> 1.3'
|
||||
gem 'omniauth-rails_csrf_protection'
|
||||
|
||||
gem 'angularjs-rails', '1.8.0'
|
||||
gem 'bugsnag'
|
||||
@@ -86,16 +83,16 @@ gem 'actionpack-action_caching'
|
||||
# AMS is deprecated, we will introduce an alternative at some point
|
||||
gem "active_model_serializers", "0.8.4"
|
||||
gem 'activerecord-session_store'
|
||||
gem 'acts-as-taggable-on'
|
||||
gem 'acts-as-taggable-on', '~> 8.1'
|
||||
gem 'angularjs-file-upload-rails', '~> 2.4.1'
|
||||
gem 'bigdecimal', '3.0.2'
|
||||
gem 'bootsnap', require: false
|
||||
gem 'geocoder'
|
||||
gem 'gmaps4rails'
|
||||
gem 'mimemagic', '> 0.3.5'
|
||||
gem 'paper_trail'
|
||||
gem 'paper_trail', '~> 12.1.0'
|
||||
gem 'rack-rewrite'
|
||||
gem 'rack-timeout'
|
||||
gem 'rack-ssl', require: 'rack/ssl'
|
||||
gem 'roadie-rails'
|
||||
|
||||
gem 'hiredis'
|
||||
@@ -104,27 +101,33 @@ gem 'redis', '>= 4.0', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'sidekiq'
|
||||
gem 'sidekiq-scheduler'
|
||||
|
||||
gem "cable_ready", "5.0.1"
|
||||
gem "stimulus_reflex", "3.5.0.rc3"
|
||||
gem "cable_ready", "5.0.0.pre9"
|
||||
gem "stimulus_reflex", "3.5.0.pre9"
|
||||
|
||||
gem 'combine_pdf'
|
||||
gem 'wicked_pdf'
|
||||
gem 'wkhtmltopdf-binary'
|
||||
|
||||
gem 'immigrant'
|
||||
gem 'roo' # read spreadsheets
|
||||
gem 'spreadsheet_architect' # write spreadsheets
|
||||
gem 'roo'
|
||||
gem 'spreadsheet_architect'
|
||||
|
||||
gem 'whenever', require: false
|
||||
|
||||
gem 'test-unit', '~> 3.5'
|
||||
|
||||
gem 'coffee-rails', '~> 5.0.0'
|
||||
|
||||
gem 'mini_racer', '0.4.0'
|
||||
|
||||
gem 'angular_rails_csrf'
|
||||
|
||||
gem 'jquery-rails', '4.4.0'
|
||||
gem 'jquery-ui-rails', '~> 4.2'
|
||||
gem "select2-rails", github: "openfoodfoundation/select2-rails", branch: "v349_with_thor_v1"
|
||||
|
||||
gem 'ofn-qz', github: 'openfoodfoundation/ofn-qz', branch: 'ofn-rails-4'
|
||||
|
||||
gem 'good_migrations'
|
||||
|
||||
gem 'flipper'
|
||||
@@ -134,16 +137,11 @@ gem 'flipper-ui'
|
||||
gem "view_component"
|
||||
gem 'view_component_reflex', '3.1.14.pre9'
|
||||
|
||||
# mini_portile2 is needed when installing with Vargant
|
||||
# https://openfoodnetwork.slack.com/archives/CEBMTRCNS/p1668439152992899
|
||||
gem 'mini_portile2', '~> 2.8'
|
||||
|
||||
gem "faraday"
|
||||
gem "private_address_check"
|
||||
|
||||
gem 'newrelic_rpm'
|
||||
|
||||
group :production, :staging do
|
||||
gem 'ddtrace'
|
||||
gem 'rack-timeout'
|
||||
gem 'sd_notify' # For better Systemd process management. Used by Puma.
|
||||
end
|
||||
|
||||
@@ -152,7 +150,6 @@ group :test, :development do
|
||||
gem 'capybara'
|
||||
gem 'cuprite'
|
||||
gem 'database_cleaner', require: false
|
||||
gem 'debug', '>= 1.0.0'
|
||||
gem "factory_bot_rails", '6.2.0', require: false
|
||||
gem 'fuubar', '~> 2.5.1'
|
||||
gem 'json_spec', '~> 1.1.4'
|
||||
@@ -160,10 +157,10 @@ group :test, :development do
|
||||
gem 'letter_opener', '>= 1.4.1'
|
||||
gem 'rspec-rails', ">= 3.5.2"
|
||||
gem 'rspec-retry', require: false
|
||||
gem 'rswag'
|
||||
gem 'rswag-specs'
|
||||
gem 'shoulda-matchers'
|
||||
gem 'stimulus_reflex_testing'
|
||||
gem 'timecop'
|
||||
gem 'debug', '>= 1.0.0'
|
||||
end
|
||||
|
||||
group :test do
|
||||
@@ -178,16 +175,16 @@ end
|
||||
|
||||
group :development do
|
||||
gem 'debugger-linecache'
|
||||
gem 'rails-erd'
|
||||
gem 'foreman'
|
||||
gem 'listen'
|
||||
gem 'pry', '~> 0.13.0'
|
||||
gem 'query_count'
|
||||
gem 'rails-erd'
|
||||
gem 'rubocop'
|
||||
gem 'rubocop-rails'
|
||||
gem 'spring'
|
||||
gem 'spring-commands-rspec'
|
||||
gem 'web-console'
|
||||
|
||||
|
||||
gem 'rack-mini-profiler', '< 3.0.0'
|
||||
end
|
||||
|
||||
680
Gemfile.lock
680
Gemfile.lock
File diff suppressed because it is too large
Load Diff
4
Procfile
4
Procfile
@@ -1,5 +1,5 @@
|
||||
# Foreman Procfile. Start all dev server processes with: `foreman start`
|
||||
|
||||
rails: DEV_CACHING=true bundle exec rails s -p 3000
|
||||
rails: bundle exec rails s -p 3000
|
||||
webpack: ./bin/webpack-dev-server
|
||||
sidekiq: DEV_CACHING=true bundle exec sidekiq -q mailers -q default
|
||||
sidekiq: bundle exec sidekiq -q mailers -q default
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
[](https://github.com/openfoodfoundation/openfoodnetwork/actions/workflows/build.yml)
|
||||
[](https://codeclimate.com/github/openfoodfoundation/openfoodnetwork)
|
||||
|
||||
# Open Food Network
|
||||
|
||||
@@ -32,7 +33,7 @@ We also have a [Super Admin Guide][super-admin-guide] to help with configuration
|
||||
|
||||
## Testing
|
||||
|
||||
If you'd like to help out with testing, please introduce yourself on the #testing channel on [Slack][slack-invite] and download the [ZenHub browser extension][zenhub] to view the development pipeline. Also, do have a look in our [Welcome New QAs board][welcome-qa] for some good first issues, both on manual and automated testing (RSpec/Capybara).
|
||||
If you'd like to help out with testing, please introduce yourself on the #testing channel on [Slack][slack-invite] and download the [ZenHub browser extension][zenhub] to view the development pipeline. Also, do have a look in our [Welcome New QAs board](https://github.com/orgs/openfoodfoundation/projects/1) for some good first issues, both on manual and automated testing (RSpec/Capybara).
|
||||
|
||||
We use [BrowserStack](https://www.browserstack.com/) as a manual testing tool. BrowserStack provides open source projects with unlimited and free of charge accounts. A big thanks to them!
|
||||
|
||||
@@ -44,13 +45,12 @@ We use [KnapsackPro](https://knapsackpro.com/) for optimal parallelisation of ou
|
||||
|
||||
## Licence
|
||||
|
||||
Copyright (c) 2012 - 2024 Open Food Foundation, released under the AGPL licence.
|
||||
Copyright (c) 2012 - 2022 Open Food Foundation, released under the AGPL licence.
|
||||
|
||||
[survey]: https://docs.google.com/a/eaterprises.com.au/forms/d/1zxR5vSiU9CigJ9cEaC8-eJLgYid8CR8er7PPH9Mc-30/edit#
|
||||
[slack-invite]: https://join.slack.com/t/openfoodnetwork/shared_invite/zt-9sjkjdlu-r02kUMP1zbrTgUhZhYPF~A
|
||||
[ofn-handbook]: https://ofn-user-guide.gitbook.io/ofn-handbook/
|
||||
[ofn-install]: https://github.com/openfoodfoundation/ofn-install
|
||||
[super-admin-guide]: https://ofn-user-guide.gitbook.io/ofn-super-admin-guide
|
||||
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/5
|
||||
[welcome-qa]: https://github.com/orgs/openfoodfoundation/projects/6
|
||||
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/2
|
||||
[zenhub]: https://www.zenhub.com/extension
|
||||
|
||||
1
Rakefile
1
Rakefile
@@ -7,3 +7,4 @@
|
||||
require_relative 'config/application'
|
||||
|
||||
Openfoodnetwork::Application.load_tasks
|
||||
|
||||
|
||||
@@ -12,6 +12,5 @@ angular.module("ofn.admin", [
|
||||
"admin.orders"
|
||||
]).config ($httpProvider, $locationProvider, $qProvider) ->
|
||||
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
|
||||
# for the next line, you should also probably check file: app/assets/javascripts/admin/utils/utils.js.coffee
|
||||
$locationProvider.hashPrefix('')
|
||||
$qProvider.errorOnUnhandledRejections(false)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
// jquery and angular
|
||||
//= require jquery2
|
||||
//= require jquery_ujs
|
||||
//= require jquery.ui.all
|
||||
//= require jquery.powertip
|
||||
//= require jquery.cookie
|
||||
@@ -68,6 +69,25 @@
|
||||
//= require textAngular.min.js
|
||||
//= require i18n/translations
|
||||
//= require darkswarm/i18n.translate.js
|
||||
//= require moment/min/moment.min.js
|
||||
//= require moment/locale/ar.js
|
||||
//= require moment/locale/ca.js
|
||||
//= require moment/locale/de.js
|
||||
//= require moment/locale/en-gb.js
|
||||
//= require moment/locale/es.js
|
||||
//= require moment/locale/fil.js
|
||||
//= require moment/locale/fr.js
|
||||
//= require moment/locale/it.js
|
||||
//= require moment/locale/nb.js
|
||||
//= require moment/locale/nl-be.js
|
||||
//= require moment/locale/pt-br.js
|
||||
//= require moment/locale/pt.js
|
||||
//= require moment/locale/ru.js
|
||||
//= require moment/locale/sv.js
|
||||
//= require moment/locale/tr.js
|
||||
//= require moment/locale/pl.js
|
||||
|
||||
//= require js-big-decimal/dist/web/js-big-decimal.min.js
|
||||
|
||||
// foundation
|
||||
//= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js
|
||||
|
||||
@@ -113,7 +113,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
(DirtyProducts.count() > 0 and confirm(t("unsaved_changes_confirmation"))) or (DirtyProducts.count() == 0)
|
||||
|
||||
editProductUrl = (product, variant) ->
|
||||
"/admin/products/" + product.id + ((if variant then "/variants/" + variant.id else "")) + "/edit"
|
||||
"/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit"
|
||||
|
||||
$scope.editWarn = (product, variant) ->
|
||||
if confirm_unsaved_changes()
|
||||
@@ -135,7 +135,6 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
display_name: null
|
||||
on_hand: null
|
||||
price: null
|
||||
tax_category_id: null
|
||||
DisplayProperties.setShowVariants product.id, true
|
||||
|
||||
|
||||
@@ -163,7 +162,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
if confirm(t("are_you_sure"))
|
||||
$http(
|
||||
method: "DELETE"
|
||||
url: "/api/v0/products/" + product.id + "/variants/" + variant.id
|
||||
url: "/api/v0/products/" + product.permalink_live + "/variants/" + variant.id
|
||||
).then (response) ->
|
||||
$scope.removeVariant(product, variant)
|
||||
else
|
||||
@@ -248,6 +247,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
else
|
||||
product.variant_unit = product.variant_unit_scale = null
|
||||
|
||||
$scope.packVariant product, product.master if product.master
|
||||
|
||||
if product.variants
|
||||
for id, variant of product.variants
|
||||
@@ -298,6 +298,7 @@ filterSubmitProducts = (productsToFilter) ->
|
||||
if product.hasOwnProperty("id")
|
||||
filteredProduct = {id: product.id}
|
||||
filteredVariants = []
|
||||
filteredMaster = null
|
||||
hasUpdatableProperty = false
|
||||
|
||||
if product.hasOwnProperty("variants")
|
||||
@@ -307,6 +308,16 @@ filterSubmitProducts = (productsToFilter) ->
|
||||
variantHasUpdatableProperty = result.hasUpdatableProperty
|
||||
filteredVariants.push filteredVariant if variantHasUpdatableProperty
|
||||
|
||||
if product.master?.hasOwnProperty("unit_value")
|
||||
filteredMaster ?= { id: product.master.id }
|
||||
filteredMaster.unit_value = product.master.unit_value
|
||||
if product.master?.hasOwnProperty("unit_description")
|
||||
filteredMaster ?= { id: product.master.id }
|
||||
filteredMaster.unit_description = product.master.unit_description
|
||||
if product.master?.hasOwnProperty("display_as")
|
||||
filteredMaster ?= { id: product.master.id }
|
||||
filteredMaster.display_as = product.master.display_as
|
||||
|
||||
if product.hasOwnProperty("sku")
|
||||
filteredProduct.sku = product.sku
|
||||
hasUpdatableProperty = true
|
||||
@@ -335,9 +346,18 @@ filterSubmitProducts = (productsToFilter) ->
|
||||
if product.hasOwnProperty("category_id")
|
||||
filteredProduct.primary_taxon_id = product.category_id
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("tax_category_id")
|
||||
filteredProduct.tax_category_id = product.tax_category_id
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("inherits_properties")
|
||||
filteredProduct.inherits_properties = product.inherits_properties
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("available_on")
|
||||
filteredProduct.available_on = product.available_on
|
||||
hasUpdatableProperty = true
|
||||
if filteredMaster?
|
||||
filteredProduct.master_attributes = filteredMaster
|
||||
hasUpdatableProperty = true
|
||||
if filteredVariants.length > 0 # Note that the name of the property changes to enable mass assignment of variants attributes with rails
|
||||
filteredProduct.variants_attributes = filteredVariants
|
||||
hasUpdatableProperty = true
|
||||
@@ -372,9 +392,6 @@ filterSubmitVariant = (variant) ->
|
||||
if variant.hasOwnProperty("display_name")
|
||||
filteredVariant.display_name = variant.display_name
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("tax_category_id")
|
||||
filteredVariant.tax_category_id = variant.tax_category_id
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("display_as")
|
||||
filteredVariant.display_as = variant.display_as
|
||||
hasUpdatableProperty = true
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
angular.module("admin.dropdown").directive "linksDropdown", ($window)->
|
||||
restrict: "C"
|
||||
scope:
|
||||
links: "="
|
||||
templateUrl: "admin/links_dropdown.html"
|
||||
@@ -55,12 +55,28 @@ angular.module("admin.enterprises")
|
||||
else
|
||||
alert ("#{manager.email}" + " " + t("is_already_manager"))
|
||||
|
||||
$scope.inviteManager = ->
|
||||
$scope.invite_errors = $scope.invite_success = null
|
||||
email = $scope.newUser
|
||||
|
||||
$http.post("/admin/manager_invitations", {email: email, enterprise_id: $scope.Enterprise.id}).then (response)->
|
||||
$scope.addManager({id: response.data.user, email: email})
|
||||
$scope.invite_success = t('user_invited', email: email)
|
||||
.catch (response) ->
|
||||
$scope.invite_errors = response.data.errors
|
||||
|
||||
$scope.resetModal = ->
|
||||
$scope.newUser = $scope.invite_errors = $scope.invite_success = null
|
||||
|
||||
$scope.removeLogo = ->
|
||||
$scope.performEnterpriseAction("removeLogo", "immediate_logo_removal_warning", "removed_logo_successfully")
|
||||
|
||||
$scope.removePromoImage = ->
|
||||
$scope.performEnterpriseAction("removePromoImage", "immediate_promo_image_removal_warning", "removed_promo_image_successfully")
|
||||
|
||||
$scope.removeTermsAndConditions = ->
|
||||
$scope.performEnterpriseAction("removeTermsAndConditions", "immediate_terms_and_conditions_removal_warning", "removed_terms_and_conditions_successfully")
|
||||
|
||||
$scope.performEnterpriseAction = (enterpriseActionName, warning_message_key, success_message_key) ->
|
||||
return unless confirm($scope.translation(warning_message_key))
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
angular.module("admin.enterprises").directive 'termsAndConditionsWarning', ($rootScope, $compile, $templateCache, DialogDefaults, $timeout) ->
|
||||
restrict: 'A'
|
||||
scope: true
|
||||
|
||||
link: (scope, element, attr) ->
|
||||
# This file input click handler will hold the browser file input dialog and show a warning modal
|
||||
scope.hold_file_input_and_show_warning_modal = (event) ->
|
||||
event.preventDefault()
|
||||
scope.template = $compile($templateCache.get('admin/modals/terms_and_conditions_warning.html'))(scope)
|
||||
if scope.template.dialog
|
||||
scope.template.dialog(DialogDefaults)
|
||||
scope.template.dialog('open')
|
||||
$rootScope.$evalAsync()
|
||||
|
||||
element.bind 'click', scope.hold_file_input_and_show_warning_modal
|
||||
|
||||
# When the user presses continue in the warning modal, we open the browser file input dialog
|
||||
scope.continue = ->
|
||||
scope.template.dialog('close')
|
||||
$rootScope.$evalAsync()
|
||||
|
||||
# unbind warning modal handler and click file input again to open the browser file input dialog
|
||||
element.unbind('click').trigger('click')
|
||||
# afterwards, bind warning modal handler again so that the warning is shown the next time
|
||||
$timeout ->
|
||||
element.bind 'click', scope.hold_file_input_and_show_warning_modal
|
||||
return
|
||||
|
||||
scope.close = ->
|
||||
scope.template.dialog('close')
|
||||
$rootScope.$evalAsync()
|
||||
return
|
||||
@@ -11,8 +11,14 @@ angular.module("admin.indexUtils").directive "objForUpdate", (switchClass, pendi
|
||||
pendingChanges.remove(scope.object().id, scope.attr)
|
||||
scope.clear()
|
||||
else
|
||||
change =
|
||||
object: scope.object()
|
||||
type: scope.type
|
||||
attr: scope.attr
|
||||
value: if value? then value else ""
|
||||
scope: scope
|
||||
scope.pending()
|
||||
addPendingChange(scope.attr, value ? "")
|
||||
pendingChanges.add(scope.object().id, scope.attr, change)
|
||||
|
||||
scope.reset = (value) ->
|
||||
scope.savedValue = value
|
||||
@@ -28,33 +34,3 @@ angular.module("admin.indexUtils").directive "objForUpdate", (switchClass, pendi
|
||||
|
||||
scope.clear = ->
|
||||
switchClass( element, "", ["update-pending", "update-error", "update-success"], false )
|
||||
|
||||
# When a list of customer is filtered and we removed the "filtered value" from a customer, we
|
||||
# want to make sure the customer is updated. IE. filtering by tag, and removing said tag.
|
||||
# Deleting the "filtered value" from a customer will remove the customer entry, thus
|
||||
# removing "objForUpdate" directive from the active scope. That means $watch won't pick up
|
||||
# the attribute changed.
|
||||
# To ensure the customer is still updated, we check on the $destroy event to see if
|
||||
# the attribute has changed, if so we queue up the change.
|
||||
scope.$on '$destroy', (value) ->
|
||||
# No update
|
||||
return if scope.object()[scope.attr] is scope.savedValue
|
||||
|
||||
# For some reason the code attribute is removed from the object when cleared, so we add
|
||||
# an emptyvalue so it gets updated properly
|
||||
if scope.attr is "code" and scope.object()[scope.attr] is undefined
|
||||
scope.object()["code"] = ""
|
||||
|
||||
# Queuing up change
|
||||
addPendingChange(scope.attr, scope.object()[scope.attr])
|
||||
|
||||
# private
|
||||
|
||||
addPendingChange = (attr, value) ->
|
||||
change =
|
||||
object: scope.object()
|
||||
type: scope.type
|
||||
attr: attr
|
||||
value: value
|
||||
scope: scope
|
||||
pendingChanges.add(scope.object().id, attr, change)
|
||||
|
||||
@@ -11,6 +11,5 @@ angular.module("admin.indexUtils").factory 'SortOptions', ->
|
||||
sortingExpr
|
||||
|
||||
toggle: (predicate) ->
|
||||
# predicate is a string or an array of strings
|
||||
@reverse = (JSON.stringify(@predicate) == JSON.stringify(predicate)) && !@reverse
|
||||
@reverse = (@predicate == predicate) && !@reverse
|
||||
@predicate = predicate
|
||||
|
||||
@@ -9,54 +9,41 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
$scope.sharedResource = false
|
||||
$scope.columns = Columns.columns
|
||||
$scope.sorting = SortOptions
|
||||
$scope.sorting.toggle("order_date")
|
||||
$scope.pagination = LineItems.pagination
|
||||
$scope.per_page_options = [
|
||||
{id: 15, name: t('js.admin.orders.index.per_page', results: 15)},
|
||||
{id: 50, name: t('js.admin.orders.index.per_page', results: 50)},
|
||||
{id: 100, name: t('js.admin.orders.index.per_page', results: 100)}
|
||||
]
|
||||
$scope.page = 1
|
||||
$scope.per_page = $scope.per_page_options[0].id
|
||||
$scope.filterByVariantId = null
|
||||
searchThrough = ["order_distributor_name",
|
||||
"order_bill_address_phone",
|
||||
"order_bill_address_firstname",
|
||||
"order_bill_address_lastname",
|
||||
"order_bill_address_full_name",
|
||||
"order_bill_address_full_name_reversed",
|
||||
"order_bill_address_full_name_with_comma",
|
||||
"order_bill_address_full_name_with_comma_reversed",
|
||||
"variant_product_supplier_name",
|
||||
"order_email",
|
||||
"order_number",
|
||||
"product_name"].join("_or_") + "_cont"
|
||||
|
||||
$scope.confirmRefresh = ->
|
||||
LineItems.allSaved() || confirm(t("unsaved_changes_warning"))
|
||||
|
||||
$scope.initStartAndEnDate = ->
|
||||
$scope.startDate = moment().startOf('day').subtract(7, 'days').format('YYYY-MM-DD')
|
||||
$scope.endDate = moment().startOf('day').format('YYYY-MM-DD')
|
||||
|
||||
$scope.resetFilters = ->
|
||||
$scope.distributorFilter = ''
|
||||
$scope.supplierFilter = ''
|
||||
$scope.orderCycleFilter = ''
|
||||
$scope.query = ''
|
||||
$scope.startDate = undefined
|
||||
$scope.endDate = undefined
|
||||
event = new CustomEvent('flatpickr:clear')
|
||||
$scope.quickSearch = ''
|
||||
$scope.initStartAndEnDate()
|
||||
event = new CustomEvent('flatpickr:change', {
|
||||
detail: {
|
||||
startDate: $scope.startDate,
|
||||
endDate: $scope.endDate
|
||||
}
|
||||
})
|
||||
window.dispatchEvent(event)
|
||||
|
||||
$scope.resetSelectFilters = ->
|
||||
$scope.resetFilters()
|
||||
$scope.refreshData()
|
||||
|
||||
$scope.fetchResults = ->
|
||||
# creates indirection in order to factorize the code between orders and bulk orders
|
||||
# used in app/views/admin/shared/_angular_per_page_controls.html.haml
|
||||
$scope.refreshData()
|
||||
|
||||
$scope.refreshData = ->
|
||||
$scope.formattedStartDate = moment($scope.startDate).format()
|
||||
$scope.formattedEndDate = moment($scope.endDate).add(1,'day').format()
|
||||
|
||||
return unless moment($scope.formattedStartDate).isValid() and moment($scope.formattedEndDate).isValid()
|
||||
|
||||
return "cancel" unless $scope.confirmRefresh()
|
||||
|
||||
$scope.loadOrders()
|
||||
$scope.loadLineItems()
|
||||
|
||||
unless $scope.initialized
|
||||
@@ -64,52 +51,57 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
|
||||
$scope.dereferenceLoadedData()
|
||||
|
||||
$scope.loadOrders = ->
|
||||
return $scope.orders = [] unless $scope.line_items.length
|
||||
$scope.setOrderCycleDateRange = ->
|
||||
start_date = OrderCycles.byID[$scope.orderCycleFilter].orders_open_at
|
||||
end_date = OrderCycles.byID[$scope.orderCycleFilter].orders_close_at
|
||||
format = "YYYY-MM-DD HH:mm:ss Z"
|
||||
$scope.startDate = moment(start_date, format).format('YYYY-MM-DD')
|
||||
$scope.endDate = moment(end_date, format).startOf('day').format('YYYY-MM-DD')
|
||||
# throw a flatpickr:change event to change the date back in the datepicker
|
||||
event = new CustomEvent('flatpickr:change', {
|
||||
detail: {
|
||||
startDate: $scope.startDate,
|
||||
endDate: $scope.endDate
|
||||
}
|
||||
})
|
||||
window.dispatchEvent(event)
|
||||
|
||||
$scope.loadOrders = ->
|
||||
RequestMonitor.load $scope.orders = Orders.index(
|
||||
"q[id_in][]": $scope.line_items.map((line_item) -> line_item.order.id)
|
||||
"q[state_not_eq]": "canceled",
|
||||
"q[shipment_state_not_eq]": "shipped",
|
||||
"q[completed_at_not_null]": "true",
|
||||
"q[distributor_id_eq]": $scope.distributorFilter,
|
||||
"q[order_cycle_id_eq]": $scope.orderCycleFilter,
|
||||
"q[completed_at_gteq]": $scope.formattedStartDate,
|
||||
"q[completed_at_lt]": $scope.formattedEndDate
|
||||
)
|
||||
|
||||
$scope.loadLineItems = ->
|
||||
[formattedStartDate, formattedEndDate] = $scope.formatDates($scope.startDate, $scope.endDate)
|
||||
|
||||
RequestMonitor.load LineItems.index(
|
||||
"q[#{searchThrough}]": $scope.query,
|
||||
"q[variant_id_eq]": $scope.filterByVariantId if $scope.filterByVariantId,
|
||||
"q[order_state_not_eq]": "canceled",
|
||||
"q[order_shipment_state_not_eq]": "shipped",
|
||||
"q[order_completed_at_not_null]": "true",
|
||||
"q[order_distributor_id_eq]": $scope.distributorFilter,
|
||||
"q[variant_product_supplier_id_eq]": $scope.supplierFilter,
|
||||
"q[order_order_cycle_id_eq]": $scope.orderCycleFilter,
|
||||
"q[order_completed_at_gteq]": if formattedStartDate then formattedStartDate else undefined,
|
||||
"q[order_completed_at_lt]": if formattedEndDate then formattedEndDate else undefined,
|
||||
"q[s]": "order_completed_at desc",
|
||||
"page": $scope.page,
|
||||
"per_page": $scope.per_page
|
||||
"q[order_completed_at_gteq]": $scope.formattedStartDate,
|
||||
"q[order_completed_at_lt]": $scope.formattedEndDate
|
||||
)
|
||||
|
||||
$scope.formatDates = (startDate, endDate) ->
|
||||
formattedStartDate = moment(startDate).format('YYYY-MM-DD') if startDate
|
||||
formattedEndDate = moment(endDate).add(1,'day').format('YYYY-MM-DD') if endDate
|
||||
return [formattedStartDate, formattedEndDate]
|
||||
|
||||
$scope.loadAssociatedData = ->
|
||||
RequestMonitor.load $scope.distributors = Enterprises.index(action: "visible", ams_prefix: "basic", "q[sells_in][]": ["own", "any"])
|
||||
RequestMonitor.load $scope.orderCycles = OrderCycles.index(ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{moment().subtract(90,'days').format()}")
|
||||
RequestMonitor.load $scope.suppliers = Enterprises.index(action: "visible", ams_prefix: "basic", "q[is_primary_producer_eq]": "true")
|
||||
|
||||
$scope.dereferenceLoadedData = ->
|
||||
RequestMonitor.load $q.all([$scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.line_items.$promise]).then ->
|
||||
RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.line_items.$promise]).then ->
|
||||
Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID
|
||||
Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.byID
|
||||
Dereferencer.dereferenceAttr $scope.line_items, "supplier", Enterprises.byID
|
||||
$scope.loadOrders()
|
||||
RequestMonitor.load $q.all([$scope.orders.$promise]).then ->
|
||||
Dereferencer.dereferenceAttr $scope.line_items, "order", Orders.byID
|
||||
Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID
|
||||
Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.byID
|
||||
$scope.bulk_order_form.$setPristine()
|
||||
StatusMessage.clear()
|
||||
Dereferencer.dereferenceAttr $scope.line_items, "order", Orders.byID
|
||||
$scope.bulk_order_form.$setPristine()
|
||||
StatusMessage.clear()
|
||||
|
||||
unless $scope.initialized
|
||||
$scope.initialized = true
|
||||
@@ -129,16 +121,16 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
else
|
||||
StatusMessage.display 'failure', t "unsaved_changes_error"
|
||||
|
||||
$scope.cancelOrder = (order, sendEmailCancellation, restock_items) ->
|
||||
$scope.cancelOrder = (order, sendEmailCancellation) ->
|
||||
return $http(
|
||||
method: 'GET'
|
||||
url: "/admin/orders/#{order.number}/fire?e=cancel&send_cancellation_email=#{sendEmailCancellation}&restock_items=#{restock_items}")
|
||||
url: "/admin/orders/#{order.number}/fire?e=cancel&send_cancellation_email=#{sendEmailCancellation}")
|
||||
|
||||
$scope.deleteLineItem = (lineItem) ->
|
||||
if lineItem.order.item_count == 1
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) ->
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation) ->
|
||||
if confirm
|
||||
$scope.cancelOrder(lineItem.order, sendEmailCancellation, restock_items).then(->
|
||||
$scope.cancelOrder(lineItem.order, sendEmailCancellation).then(->
|
||||
$scope.refreshData()
|
||||
)
|
||||
else
|
||||
@@ -160,18 +152,14 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
willCancelOrders = true if (order.item_count == itemsPerOrder.get(order).length)
|
||||
|
||||
if willCancelOrders
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) ->
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation) ->
|
||||
if confirm
|
||||
itemsPerOrder.forEach (items, order) =>
|
||||
if order.item_count == items.length
|
||||
$scope.cancelOrder(order, sendEmailCancellation, restock_items).then(-> $scope.refreshData())
|
||||
$scope.cancelOrder(order, sendEmailCancellation).then(-> $scope.refreshData())
|
||||
else
|
||||
Promise.all(LineItems.delete(item) for item in items).then(-> $scope.refreshData())
|
||||
, "js.admin.deleting_item_will_cancel_order")
|
||||
else
|
||||
ofnDeleteLineItemsAlert(() ->
|
||||
Promise.all(LineItems.delete(item) for item in lineItemsToDelete).then(-> $scope.refreshData())
|
||||
, lineItemsToDelete.length)
|
||||
|
||||
$scope.allBoxesChecked = ->
|
||||
checkedCount = $scope.filteredLineItems.reduce (count,lineItem) ->
|
||||
@@ -186,17 +174,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
$scope.setSelectedUnitsVariant = (unitsProduct,unitsVariant) ->
|
||||
$scope.selectedUnitsProduct = unitsProduct
|
||||
$scope.selectedUnitsVariant = unitsVariant
|
||||
$scope.filterByVariantId = unitsVariant.id
|
||||
$scope.page = 1
|
||||
$scope.refreshData()
|
||||
|
||||
$scope.resetSelectedUnitsVariant = ->
|
||||
$scope.selectedUnitsProduct = { }
|
||||
$scope.selectedUnitsVariant = { }
|
||||
$scope.filterByVariantId = null
|
||||
$scope.sharedResource = false
|
||||
$scope.page = 1
|
||||
$scope.refreshData()
|
||||
|
||||
$scope.getLineItemScale = (lineItem) ->
|
||||
if lineItem.units_product && lineItem.units_variant && (lineItem.units_product.variant_unit == "weight" || lineItem.units_product.variant_unit == "volume")
|
||||
@@ -242,7 +219,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
|
||||
$scope.getGroupBySizeFormattedValueWithUnitName = (value, unitsProduct, unitsVariant) ->
|
||||
scale = $scope.getScale(unitsProduct, unitsVariant)
|
||||
if scale && value
|
||||
if scale
|
||||
value = value / scale if scale != 28.35 && scale != 1 && scale != 453.6 # divide by scale if not smallest unit
|
||||
$scope.getFormattedValueWithUnitName(value, unitsProduct, unitsVariant, scale)
|
||||
else
|
||||
@@ -286,8 +263,4 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
lineItem.final_weight_volume = LineItems.pristineByID[lineItem.id].final_weight_volume * lineItem.quantity / LineItems.pristineByID[lineItem.id].quantity
|
||||
$scope.weightAdjustedPrice(lineItem)
|
||||
|
||||
$scope.changePage = (newPage) ->
|
||||
$scope.page = newPage
|
||||
$scope.refreshData()
|
||||
|
||||
$scope.resetSelectFilters()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
angular.module("admin.lineItems").filter "variantFilter", ->
|
||||
return (lineItems,selectedUnitsProduct,selectedUnitsVariant,sharedResource) ->
|
||||
filtered = []
|
||||
filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedUnitsProduct,{}) ||
|
||||
(lineItem.units_product.id == selectedUnitsProduct.id && (sharedResource || lineItem.units_variant.id == selectedUnitsVariant.id ) ) )
|
||||
filtered
|
||||
@@ -18,6 +18,7 @@ angular.module('admin.orderCycles')
|
||||
|
||||
$scope.submit = ($event, destination) ->
|
||||
$event.preventDefault()
|
||||
NavigationCheck.clear()
|
||||
StatusMessage.display 'progress', t('js.saving')
|
||||
OrderCycle.update(destination, $scope.order_cycle_form)
|
||||
|
||||
|
||||
@@ -35,11 +35,7 @@ angular.module('admin.orderCycles')
|
||||
OrderCycle.removeExchangeFee(exchange, index)
|
||||
$scope.order_cycle_form.$dirty = true
|
||||
|
||||
$scope.setPickupTimeFieldDirty = (index, pickup_time) ->
|
||||
# if the pickup_time is already set we are in edit mode, so no need to set pickup_time field as dirty
|
||||
# to show it is required (it has a red border when set to dirty)
|
||||
return if pickup_time
|
||||
|
||||
$scope.setPickupTimeFieldDirty = (index) ->
|
||||
$timeout ->
|
||||
pickup_time_field_name = "order_cycle_outgoing_exchange_" + index + "_pickup_time"
|
||||
$scope.order_cycle_form[pickup_time_field_name].$setDirty()
|
||||
|
||||
@@ -33,11 +33,9 @@ angular.module("admin.orderCycles").controller "OrderCyclesCtrl", ($scope, $q, C
|
||||
StatusMessage.display 'notice', "You have unsaved changes" if newVal
|
||||
|
||||
$scope.showMore = (days) ->
|
||||
orderCycles = OrderCycles.index(ams_prefix: "index",
|
||||
"q[orders_close_at_gt]": "#{daysFromToday($scope.ordersCloseAtLimit - days)}",
|
||||
"q[orders_close_at_lteq]": "#{daysFromToday($scope.ordersCloseAtLimit)}"
|
||||
)
|
||||
$scope.ordersCloseAtLimit -= days
|
||||
existingIDs = Object.keys(OrderCycles.byID)
|
||||
orderCycles = OrderCycles.index(ams_prefix: "index", "q[orders_close_at_gt]": "#{daysFromToday($scope.ordersCloseAtLimit)}", "q[id_not_in][]": existingIDs)
|
||||
orderCycles.$promise.then ->
|
||||
$scope.orderCycles.push(orderCycle) for orderCycle in orderCycles
|
||||
compileData()
|
||||
|
||||
@@ -33,4 +33,6 @@ angular.module('admin.orderCycles').factory('Enterprise', ($resource) ->
|
||||
variantsOf: (product) ->
|
||||
if product.variants.length > 0
|
||||
variant.id for variant in product.variants
|
||||
else
|
||||
[product.master_id]
|
||||
})
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
angular.module("admin.orders").controller "bulkCancelCtrl", ($scope, $http, $timeout) ->
|
||||
|
||||
$scope.cancelOrder = (orderIds, sendEmailCancellation, restock_items) ->
|
||||
$http(
|
||||
method: 'post'
|
||||
url: "/admin/orders/bulk_cancel?order_ids=#{orderIds}&send_cancellation_email=#{sendEmailCancellation}&restock_items=#{restock_items}" ).then(->
|
||||
window.location.reload()
|
||||
)
|
||||
|
||||
$scope.cancelSelectedOrders = ->
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) ->
|
||||
if confirm
|
||||
$scope.cancelOrder $scope.selected_orders, sendEmailCancellation, restock_items
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
angular.module("admin.orders").controller "bulkInvoiceCtrl", ($scope, $http, $timeout) ->
|
||||
$scope.createBulkInvoice = ->
|
||||
$scope.invoice_id = null
|
||||
$scope.poll = 1
|
||||
$scope.loading = true
|
||||
$scope.message = null
|
||||
$scope.error = null
|
||||
$scope.poll_wait = 5 # 5 Seconds between each check
|
||||
$scope.poll_retries = 80 # Maximum checks before stopping
|
||||
|
||||
$http.post('/admin/orders/invoices', {order_ids: $scope.selected_orders}).then (response) ->
|
||||
$scope.invoice_id = response.data
|
||||
$scope.pollBulkInvoice()
|
||||
|
||||
$scope.pollBulkInvoice = ->
|
||||
$timeout($scope.nextPoll, $scope.poll_wait * 1000)
|
||||
|
||||
$scope.nextPoll = ->
|
||||
$http.get('/admin/orders/invoices/'+$scope.invoice_id+'/poll').then (response) ->
|
||||
$scope.loading = false
|
||||
$scope.message = t('js.admin.orders.index.bulk_invoice_created')
|
||||
|
||||
.catch (response) ->
|
||||
$scope.poll++
|
||||
|
||||
if $scope.poll > $scope.poll_retries
|
||||
$scope.loading = false
|
||||
$scope.error = t('js.admin.orders.index.bulk_invoice_failed')
|
||||
return
|
||||
|
||||
$scope.pollBulkInvoice()
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, RequestMonitor, Orders, SortOptions, $window, $filter, $location, KeyValueMapStore) ->
|
||||
$scope.RequestMonitor = RequestMonitor
|
||||
$scope.pagination = Orders.pagination
|
||||
$scope.orders = Orders.all
|
||||
$scope.sortOptions = SortOptions
|
||||
$scope.per_page_options = [
|
||||
{id: 15, name: t('js.admin.orders.index.per_page', results: 15)},
|
||||
{id: 50, name: t('js.admin.orders.index.per_page', results: 50)},
|
||||
{id: 100, name: t('js.admin.orders.index.per_page', results: 100)}
|
||||
]
|
||||
$scope.selected_orders = []
|
||||
$scope.checkboxes = {}
|
||||
$scope.selected = false
|
||||
$scope.select_all = false
|
||||
$scope.poll = 0
|
||||
$scope.rowStatus = {}
|
||||
|
||||
KeyValueMapStore.localStorageKey = 'ordersFilters'
|
||||
KeyValueMapStore.storableKeys = ["q", "sorting", "page", "per_page"]
|
||||
|
||||
$scope.initialise = ->
|
||||
unless KeyValueMapStore.restoreValues($scope)
|
||||
$scope.setDefaults()
|
||||
|
||||
$scope.fetchResults()
|
||||
|
||||
$scope.setDefaults = ->
|
||||
$scope.per_page = 15
|
||||
$scope.q = {
|
||||
completed_at_not_null: true
|
||||
}
|
||||
e = new CustomEvent("flatpickr_clear");
|
||||
window.dispatchEvent(e)
|
||||
|
||||
$scope.clearFilters = () ->
|
||||
KeyValueMapStore.clearKeyValueMap()
|
||||
$scope.setDefaults()
|
||||
$scope.fetchResults()
|
||||
|
||||
$scope.fetchResults = (page=1) ->
|
||||
startDateWithTime = $scope.appendStringIfNotEmpty($scope.q?.completed_at_gteq, ' 00:00:00')
|
||||
endDateWithTime = $scope.appendStringIfNotEmpty($scope.q?.completed_at_lteq, ' 23:59:59')
|
||||
|
||||
$scope.resetSelected()
|
||||
params = {
|
||||
'q[completed_at_gteq]': startDateWithTime,
|
||||
'q[completed_at_lteq]': endDateWithTime,
|
||||
'q[state_eq]': $scope.q?.state_eq,
|
||||
'q[number_cont]': $scope.q?.number_cont,
|
||||
'q[email_cont]': $scope.q?.email_cont,
|
||||
'q[bill_address_firstname_start]': $scope.q?.bill_address_firstname_start,
|
||||
'q[bill_address_lastname_start]': $scope.q?.bill_address_lastname_start,
|
||||
# Set default checkbox values to null. See: https://github.com/openfoodfoundation/openfoodnetwork/pull/3076#issuecomment-440010498
|
||||
'q[completed_at_not_null]': $scope.q?.completed_at_not_null || null,
|
||||
'q[distributor_id_in][]': $scope.q?.distributor_id_in,
|
||||
'q[order_cycle_id_in][]': $scope.q?.order_cycle_id_in,
|
||||
'q[s]': $scope.sorting || 'completed_at desc',
|
||||
shipping_method_id: $scope.q?.shipping_method_id,
|
||||
per_page: $scope.per_page,
|
||||
page: page
|
||||
}
|
||||
KeyValueMapStore.setStoredValues($scope)
|
||||
RequestMonitor.load(Orders.index(params).$promise)
|
||||
|
||||
$scope.appendStringIfNotEmpty = (baseString, stringToAppend) ->
|
||||
return baseString unless baseString
|
||||
return baseString if baseString.endsWith(stringToAppend)
|
||||
|
||||
baseString + stringToAppend
|
||||
|
||||
$scope.resetSelected = ->
|
||||
$scope.selected_orders.length = 0
|
||||
$scope.selected = false
|
||||
$scope.select_all = false
|
||||
$scope.checkboxes = {}
|
||||
|
||||
$scope.toggleSelection = (id) ->
|
||||
index = $scope.selected_orders.indexOf(id)
|
||||
|
||||
if index == -1
|
||||
$scope.selected_orders.push(id)
|
||||
else
|
||||
$scope.selected_orders.splice(index, 1)
|
||||
|
||||
$scope.toggleAll = ->
|
||||
$scope.selected_orders.length = 0
|
||||
$scope.orders.forEach (order) ->
|
||||
$scope.checkboxes[order.id] = $scope.select_all
|
||||
$scope.selected_orders.push order.id if $scope.select_all
|
||||
|
||||
$scope.$watch 'sortOptions', (sort) ->
|
||||
return unless sort && sort.predicate != ""
|
||||
|
||||
$scope.sorting = sort.getSortingExpr()
|
||||
$scope.fetchResults()
|
||||
, true
|
||||
|
||||
$scope.capturePayment = (order) ->
|
||||
$scope.rowAction('capture', order)
|
||||
|
||||
$scope.shipOrder = (order) ->
|
||||
$scope.rowAction('ship', order)
|
||||
|
||||
$scope.rowAction = (action, order) ->
|
||||
$scope.rowStatus[order.id] = "loading"
|
||||
|
||||
Orders[action](order).$promise.then (data) ->
|
||||
$scope.rowStatus[order.id] = "success"
|
||||
$timeout(->
|
||||
$scope.rowStatus[order.id] = null
|
||||
, 1500)
|
||||
, (error) ->
|
||||
$scope.rowStatus[order.id] = "error"
|
||||
|
||||
$scope.changePage = (newPage) ->
|
||||
$scope.page = newPage
|
||||
$scope.fetchResults(newPage)
|
||||
@@ -0,0 +1,5 @@
|
||||
angular.module("admin.orders").directive "invoicesModal", ($modal) ->
|
||||
restrict: 'C'
|
||||
link: (scope, elem, attrs, ctrl) ->
|
||||
elem.on "click", (ev) =>
|
||||
scope.uploadModal = $modal.open(templateUrl: 'admin/modals/bulk_invoice.html', controller: ctrl, scope: scope, windowClass: 'simple-modal')
|
||||
@@ -42,10 +42,8 @@ angular.module('admin.payments').factory 'Payment', (AdminStripeElements, curren
|
||||
|
||||
submit: =>
|
||||
munged = @preprocess()
|
||||
PaymentResource.create({order_id: munged.order_id}, munged, (response, headers, status) ->
|
||||
rawHtml = Object.values(response).join('').replace('[object Object]true', '')
|
||||
document.body.innerHTML = rawHtml
|
||||
$window.history.pushState({}, '', "/admin/orders/" + munged.order_id + "/payments")
|
||||
PaymentResource.create({order_id: munged.order_id}, munged, (response, headers, status)=>
|
||||
$window.location.pathname = "/admin/orders/" + munged.order_id + "/payments"
|
||||
, (response) ->
|
||||
StatusMessage.display 'error', t("spree.admin.payments.source_forms.stripe.error_saving_payment")
|
||||
)
|
||||
|
||||
@@ -2,9 +2,6 @@ angular.module("admin.products").factory "VariantUnitManager", (availableUnits)
|
||||
class VariantUnitManager
|
||||
@units:
|
||||
'weight':
|
||||
0.001:
|
||||
name: 'mg'
|
||||
system: 'metric'
|
||||
1.0:
|
||||
name: 'g'
|
||||
system: 'metric'
|
||||
@@ -24,21 +21,12 @@ angular.module("admin.products").factory "VariantUnitManager", (availableUnits)
|
||||
0.001:
|
||||
name: 'mL'
|
||||
system: 'metric'
|
||||
0.01:
|
||||
name: 'cL'
|
||||
system: 'metric'
|
||||
0.1:
|
||||
name: 'dL'
|
||||
system: 'metric'
|
||||
1.0:
|
||||
name: 'L'
|
||||
system: 'metric'
|
||||
1000.0:
|
||||
name: 'kL'
|
||||
system: 'metric'
|
||||
4.54609:
|
||||
name: 'gal'
|
||||
system: 'metric'
|
||||
'items':
|
||||
1:
|
||||
name: 'items'
|
||||
@@ -72,13 +60,8 @@ angular.module("admin.products").factory "VariantUnitManager", (availableUnits)
|
||||
|
||||
@compatibleUnitScales: (scale, unitType) ->
|
||||
scaleSystem = @units[unitType][scale]['system']
|
||||
if availableUnits
|
||||
available = availableUnits.split(",")
|
||||
(parseFloat(scale) for scale, scaleInfo of @units[unitType] when scaleInfo['system'] == scaleSystem and available.includes(scaleInfo['name'])).sort (a, b) ->
|
||||
a - b
|
||||
else
|
||||
(parseFloat(scale) for scale, scaleInfo of @units[unitType] when scaleInfo['system'] == scaleSystem).sort (a, b) ->
|
||||
a - b
|
||||
(parseFloat(scale) for scale, scaleInfo of @units[unitType] when scaleInfo['system'] == scaleSystem).sort (a, b) ->
|
||||
a - b
|
||||
|
||||
@systemOfMeasurement: (scale, unitType) ->
|
||||
if @units[unitType][scale]
|
||||
|
||||
@@ -14,4 +14,7 @@ angular.module("admin.resources").factory 'EnterpriseResource', ($resource) ->
|
||||
'removePromoImage':
|
||||
url: '/api/v0/enterprises/:id/promo_image.json'
|
||||
method: 'DELETE'
|
||||
'removeTermsAndConditions':
|
||||
url: '/api/v0/enterprises/:id/terms_and_conditions.json'
|
||||
method: 'DELETE'
|
||||
})
|
||||
|
||||
@@ -63,3 +63,4 @@ angular.module("admin.resources").factory 'Enterprises', ($q, $filter, Enterpris
|
||||
|
||||
removeLogo: performActionOnEnterpriseResource(EnterpriseResource.removeLogo)
|
||||
removePromoImage: performActionOnEnterpriseResource(EnterpriseResource.removePromoImage)
|
||||
removeTermsAndConditions: performActionOnEnterpriseResource(EnterpriseResource.removeTermsAndConditions)
|
||||
|
||||
@@ -32,6 +32,9 @@ jQuery(function($) {
|
||||
});
|
||||
}
|
||||
|
||||
// Make flash messages dissapear
|
||||
setTimeout('$(".flash").fadeOut()', 5000);
|
||||
|
||||
// Highlight hovered table column
|
||||
$('table tbody tr td.actions a').hover(function(){
|
||||
var tr = $(this).closest('tr');
|
||||
|
||||
15
app/assets/javascripts/admin/spree/images/index.js.coffee
Normal file
15
app/assets/javascripts/admin/spree/images/index.js.coffee
Normal file
@@ -0,0 +1,15 @@
|
||||
$ ->
|
||||
($ '#new_image_link').click (event) ->
|
||||
event.preventDefault()
|
||||
|
||||
($ '.no-objects-found').hide()
|
||||
|
||||
($ this).hide()
|
||||
$.ajax
|
||||
type: 'GET'
|
||||
url: @href
|
||||
data: (
|
||||
authenticity_token: AUTH_TOKEN
|
||||
)
|
||||
success: (r) ->
|
||||
($ '#images').html r
|
||||
7
app/assets/javascripts/admin/spree/images/new.js.coffee
Normal file
7
app/assets/javascripts/admin/spree/images/new.js.coffee
Normal file
@@ -0,0 +1,7 @@
|
||||
($ '#cancel_link').click (event) ->
|
||||
event.preventDefault()
|
||||
|
||||
($ '.no-objects-found').show()
|
||||
|
||||
($ '#new_image_link').show()
|
||||
($ '#images').html('')
|
||||
@@ -15,7 +15,7 @@ $(document).ready(function() {
|
||||
console.log(msg);
|
||||
});
|
||||
}
|
||||
$('.admin-order-edit-form a.ship').click(handle_ship_click);
|
||||
$('[data-hook=admin_order_edit_form] a.ship').click(handle_ship_click);
|
||||
|
||||
//handle shipping method edit click
|
||||
$('a.edit-method').click(toggleMethodEdit);
|
||||
@@ -37,7 +37,7 @@ $(document).ready(function() {
|
||||
console.log(msg);
|
||||
});
|
||||
}
|
||||
$('.admin-order-edit-form a.save-method').click(handle_shipping_method_save);
|
||||
$('[data-hook=admin_order_edit_form] a.save-method').click(handle_shipping_method_save);
|
||||
|
||||
//handle tracking info edit/delete
|
||||
|
||||
@@ -64,8 +64,8 @@ $(document).ready(function() {
|
||||
return Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipmentNumber + ".json");
|
||||
}
|
||||
|
||||
$('.admin-order-edit-form a.save-tracking').click(saveTrackingInfo);
|
||||
$('.admin-order-edit-form a.delete-tracking').click(deleteTrackingInfo);
|
||||
$('[data-hook=admin_order_edit_form] a.save-tracking').click(saveTrackingInfo);
|
||||
$('[data-hook=admin_order_edit_form] a.delete-tracking').click(deleteTrackingInfo);
|
||||
|
||||
// handle note edit/delete
|
||||
|
||||
@@ -96,8 +96,8 @@ $(document).ready(function() {
|
||||
});
|
||||
}
|
||||
|
||||
$('.admin-order-edit-form a.save-note').click(saveNote);
|
||||
$('.admin-order-edit-form a.delete-note').click(deleteNote);
|
||||
$('[data-hook=admin_order_edit_form] a.save-note').click(saveNote);
|
||||
$('[data-hook=admin_order_edit_form] a.delete-note').click(deleteNote);
|
||||
|
||||
// Makes API call for notes/tracking info
|
||||
makeApiCall = function(url, params) {
|
||||
|
||||
@@ -4,8 +4,7 @@ $(document).ready(function() {
|
||||
|
||||
initAlert()
|
||||
initConfirm()
|
||||
initButtonCancel()
|
||||
initLinkCancel()
|
||||
initCancelOrder()
|
||||
|
||||
if ($('#variant_autocomplete_template').length > 0) {
|
||||
window.variantTemplate = Handlebars.compile($('#variant_autocomplete_template').text());
|
||||
@@ -50,11 +49,11 @@ $(document).ready(function() {
|
||||
if (quantity > maxQuantity) {
|
||||
quantity = maxQuantity;
|
||||
save.parents('tr').find('input.line_item_quantity').val(maxQuantity);
|
||||
ofnAlert(t("js.admin.orders.quantity_unavailable"));
|
||||
} else {
|
||||
adjustItems(shipment_number, variant_id, quantity, true);
|
||||
ofnAlert(t("js.admin.orders.quantity_adjusted"));
|
||||
}
|
||||
toggleItemEdit();
|
||||
|
||||
adjustItems(shipment_number, variant_id, quantity, true);
|
||||
return false;
|
||||
}
|
||||
$('a.save-item').click(handle_save_click);
|
||||
@@ -100,7 +99,6 @@ adjustItems = function(shipment_number, variant_id, quantity, restock_item){
|
||||
doAdjustItems(shipment_number, variant_id, quantity, inventory_units, restock_item, () => {
|
||||
var redirectTo = new URL(Spree.routes.cancel_order.toString());
|
||||
redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
|
||||
redirectTo.searchParams.append("restock_item", restock_item);
|
||||
window.location.href = redirectTo.toString();
|
||||
});
|
||||
}
|
||||
@@ -252,18 +250,6 @@ ofnCancelOrderAlert = function(callback, i18nKey) {
|
||||
$('#custom-confirm').show();
|
||||
}
|
||||
|
||||
ofnDeleteLineItemsAlert = function(callback, count) {
|
||||
$('#custom-confirm .message').html(`${t("js.admin.orders.delete_line_items_html", {count: count})}`);
|
||||
$('#custom-confirm button.confirm').click(() => {
|
||||
$('#custom-confirm').hide();
|
||||
callback();
|
||||
});
|
||||
$('#custom-confirm button.cancel').click(() => {
|
||||
$('#custom-confirm').hide();
|
||||
});
|
||||
$('#custom-confirm').show();
|
||||
}
|
||||
|
||||
ofnConfirm = function(callback) {
|
||||
$('#custom-confirm .message').html(
|
||||
` ${t("are_you_sure")}
|
||||
@@ -278,23 +264,17 @@ ofnConfirm = function(callback) {
|
||||
$('#custom-confirm').show();
|
||||
}
|
||||
|
||||
initCancelAction = function(e){
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) => {
|
||||
if (confirm) {
|
||||
var redirectTo = new URL(Spree.routes.cancel_order.toString());
|
||||
redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
|
||||
redirectTo.searchParams.append("restock_items", restock_items);
|
||||
window.location.href = redirectTo.toString();
|
||||
}
|
||||
initCancelOrder = function() {
|
||||
$('#cancel_order_form').submit(function(e){
|
||||
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) => {
|
||||
if (confirm) {
|
||||
var redirectTo = new URL(Spree.routes.cancel_order.toString());
|
||||
redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
|
||||
redirectTo.searchParams.append("restock_items", restock_items);
|
||||
window.location.href = redirectTo.toString();
|
||||
}
|
||||
});
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
e.preventDefault();
|
||||
return false;
|
||||
};
|
||||
|
||||
initButtonCancel = function() {
|
||||
$('#cancel_order_form').submit(initCancelAction)
|
||||
}
|
||||
|
||||
initLinkCancel = function() {
|
||||
$('#links-dropdown a[href$="cancel"]').click(initCancelAction);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ handle_move = (e, data) ->
|
||||
node = data.rslt.o
|
||||
new_parent = data.rslt.np
|
||||
|
||||
url = new URL(Spree.routes.admin_taxonomy_taxons)
|
||||
url = new URL(base_url)
|
||||
url.pathname = url.pathname + '/' + node.attr("id")
|
||||
data = {
|
||||
_method: "put",
|
||||
|
||||
@@ -10,12 +10,6 @@ angular.module("admin.utils").directive "variantAutocomplete", ($timeout) ->
|
||||
element.select2
|
||||
placeholder: t('admin.orders.select_variant')
|
||||
minimumInputLength: 3
|
||||
formatInputTooShort: ->
|
||||
t('admin.select2.minimal_search_length', count: 3)
|
||||
formatSearching: ->
|
||||
t('admin.select2.searching')
|
||||
formatNoMatches: ->
|
||||
t('admin.select2.no_matches')
|
||||
ajax:
|
||||
url: Spree.routes.variants_search
|
||||
datatype: "json"
|
||||
|
||||
@@ -5,8 +5,8 @@ angular.module("admin.utils").factory "StatusMessage", ->
|
||||
alert: {style: {color: 'grey'}}
|
||||
notice: {style: {color: 'grey'}}
|
||||
success: {style: {color: '#9fc820'}}
|
||||
failure: {style: {color: '#C85136'}}
|
||||
error: {style: {color: '#C85136'}}
|
||||
failure: {style: {color: '#da5354'}}
|
||||
error: {style: {color: '#da5354'}}
|
||||
|
||||
statusMessage:
|
||||
text: ""
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider, $locationProvider) ->
|
||||
# for the next line, you should also probably check file: app/assets/javascripts/admin/admin_ofn.js.coffee
|
||||
$locationProvider.hashPrefix('')
|
||||
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider) ->
|
||||
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// This is a manifest file that'll be compiled into including all the files listed below.
|
||||
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
|
||||
// be included in the compiled file accessible from http://example.com/assets/application.js
|
||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
// the compiled file.
|
||||
//
|
||||
|
||||
//= require jquery2
|
||||
//= require admin/spree/spree-select2
|
||||
//= require admin/spree/handlebar_extensions
|
||||
|
||||
//= require i18n/translations
|
||||
//= require darkswarm/i18n.translate.js
|
||||
|
||||
window.angular = { module: function(noop){ return { value: function(){} } } }
|
||||
@@ -29,6 +29,24 @@
|
||||
#
|
||||
#= require angular-flash.min.js
|
||||
#
|
||||
#= require moment/min/moment.min.js
|
||||
#= require moment/locale/ar.js
|
||||
#= require moment/locale/ca.js
|
||||
#= require moment/locale/de.js
|
||||
#= require moment/locale/en-gb.js
|
||||
#= require moment/locale/es.js
|
||||
#= require moment/locale/fil.js
|
||||
#= require moment/locale/fr.js
|
||||
#= require moment/locale/it.js
|
||||
#= require moment/locale/nb.js
|
||||
#= require moment/locale/nl-be.js
|
||||
#= require moment/locale/pt-br.js
|
||||
#= require moment/locale/pt.js
|
||||
#= require moment/locale/ru.js
|
||||
#= require moment/locale/sv.js
|
||||
#= require moment/locale/tr.js
|
||||
#= require moment/locale/pl.js
|
||||
#
|
||||
#= require modernizr
|
||||
#
|
||||
#= require foundation-sites/js/foundation.js
|
||||
@@ -49,3 +67,11 @@ document.addEventListener "turbo:before-render", ->
|
||||
rootscope = null
|
||||
window.injector = null
|
||||
true
|
||||
|
||||
document.addEventListener "ajax:beforeSend", (event) =>
|
||||
window.Turbo.navigator.adapter.progressBar.setValue(0)
|
||||
window.Turbo.navigator.adapter.progressBar.show()
|
||||
|
||||
document.addEventListener "ajax:complete", (event) =>
|
||||
window.Turbo.navigator.adapter.progressBar.setValue(100)
|
||||
window.Turbo.navigator.adapter.progressBar.hide()
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
angular.module('Darkswarm').controller "AccordionCtrl", ($scope, localStorageService, $timeout, $document, CurrentHub) ->
|
||||
$scope.accordionSections = ["details", "billing", "shipping", "payment"]
|
||||
$scope.accordion = { details: true, billing: true, shipping: true, payment: true }
|
||||
|
||||
$scope.show = (section) ->
|
||||
$scope.accordion[section] = true
|
||||
|
||||
$scope.scrollTo = (section) ->
|
||||
# Scrolling is confused by our position:fixed top bar - add an offset to scroll
|
||||
# to the correct location, plus 5px buffer
|
||||
offset_height = $("nav.top-bar").height() + 5
|
||||
$document.scrollTo($("##{section}"), offset_height, 400)
|
||||
|
||||
$scope.$on 'purchaseFormInvalid', (event, form) ->
|
||||
# Scroll to first invalid section
|
||||
for section in $scope.accordionSections
|
||||
if not form[section].$valid
|
||||
$scope.show section
|
||||
$timeout ->
|
||||
$scope.scrollTo(section)
|
||||
, 50
|
||||
break
|
||||
@@ -0,0 +1,12 @@
|
||||
angular.module('Darkswarm').controller "BillingCtrl", ($scope, $timeout, $controller) ->
|
||||
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
|
||||
|
||||
$scope.name = "billing"
|
||||
$scope.nextPanel = "shipping"
|
||||
|
||||
$scope.summary = ->
|
||||
[$scope.order.bill_address.address1,
|
||||
$scope.order.bill_address.city,
|
||||
$scope.order.bill_address.zipcode]
|
||||
|
||||
$timeout $scope.onTimeout
|
||||
@@ -0,0 +1,43 @@
|
||||
angular.module('Darkswarm').controller "CheckoutCtrl", ($scope, localStorageService, Checkout, CurrentUser, CurrentHub, $http) ->
|
||||
$scope.Checkout = Checkout
|
||||
$scope.submitted = false
|
||||
|
||||
# Bind to local storage
|
||||
$scope.fieldsToBind = ["bill_address", "email", "payment_method_id", "shipping_method_id", "ship_address"]
|
||||
prefix = "order_#{Checkout.order.id}#{CurrentUser.id or ""}#{CurrentHub.hub.id}"
|
||||
|
||||
for field in $scope.fieldsToBind
|
||||
localStorageService.bind $scope, "Checkout.order.#{field}", Checkout.order[field], "#{prefix}_#{field}"
|
||||
|
||||
localStorageService.bind $scope, "Checkout.ship_address_same_as_billing", true, "#{prefix}_sameasbilling"
|
||||
localStorageService.bind $scope, "Checkout.default_bill_address", false, "#{prefix}_defaultasbilladdress"
|
||||
localStorageService.bind $scope, "Checkout.default_ship_address", false, "#{prefix}_defaultasshipaddress"
|
||||
|
||||
$scope.order = Checkout.order # Ordering is important
|
||||
$scope.secrets = Checkout.secrets
|
||||
|
||||
$scope.enabled = !!CurrentUser.id?
|
||||
|
||||
$scope.purchase = (event, form) ->
|
||||
event.preventDefault()
|
||||
$scope.formdata = form
|
||||
$scope.submitted = true
|
||||
|
||||
if CurrentUser.id
|
||||
$scope.validateForm(form)
|
||||
else
|
||||
$scope.ensureUserIsGuest()
|
||||
|
||||
$scope.validateForm = ->
|
||||
if $scope.formdata.$valid
|
||||
$scope.Checkout.purchase()
|
||||
else
|
||||
$scope.$broadcast 'purchaseFormInvalid', $scope.formdata
|
||||
|
||||
$scope.ensureUserIsGuest = (callback = null) ->
|
||||
$http.post("/user/registered_email", {email: $scope.order.email})
|
||||
.then (response)->
|
||||
window.CableReady.perform(response.data)
|
||||
.catch ->
|
||||
$scope.validateForm() if $scope.submitted
|
||||
callback() if callback
|
||||
@@ -0,0 +1,8 @@
|
||||
angular.module('Darkswarm').controller "CountryCtrl", ($scope, availableCountries) ->
|
||||
|
||||
$scope.countries = availableCountries
|
||||
|
||||
$scope.countriesById = $scope.countries.reduce (obj, country) ->
|
||||
obj[country.id] = country
|
||||
obj
|
||||
, {}
|
||||
@@ -0,0 +1,24 @@
|
||||
angular.module('Darkswarm').controller "DetailsCtrl", ($scope, $timeout, $http, CurrentUser, SpreeUser, $controller) ->
|
||||
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
|
||||
|
||||
$scope.name = "details"
|
||||
$scope.nextPanel = "billing"
|
||||
|
||||
$scope.login_or_next = (event) ->
|
||||
event.preventDefault()
|
||||
unless CurrentUser.id
|
||||
$scope.ensureUserIsGuest($scope.next)
|
||||
return
|
||||
|
||||
$scope.next()
|
||||
|
||||
$scope.summary = ->
|
||||
[$scope.fullName(),
|
||||
$scope.order.email,
|
||||
$scope.order.bill_address.phone]
|
||||
|
||||
$scope.fullName = ->
|
||||
[$scope.order.bill_address.firstname ? null,
|
||||
$scope.order.bill_address.lastname ? null].join(" ").trim()
|
||||
|
||||
$timeout $scope.onTimeout
|
||||
@@ -0,0 +1,19 @@
|
||||
angular.module('Darkswarm').controller "PaymentCtrl", ($scope, $timeout, savedCreditCards, Dates, $controller) ->
|
||||
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
|
||||
|
||||
$scope.savedCreditCards = savedCreditCards
|
||||
$scope.name = "payment"
|
||||
$scope.months = Dates.months
|
||||
$scope.years = Dates.years
|
||||
|
||||
$scope.secrets.card_month = "1"
|
||||
$scope.secrets.card_year = moment().year()
|
||||
|
||||
for card in savedCreditCards when card.is_default
|
||||
$scope.secrets.selected_card = card.id
|
||||
break
|
||||
|
||||
$scope.summary = ->
|
||||
[$scope.Checkout.paymentMethod()?.name]
|
||||
|
||||
$timeout $scope.onTimeout
|
||||
@@ -0,0 +1,11 @@
|
||||
angular.module('Darkswarm').controller "ShippingCtrl", ($scope, $timeout, ShippingMethods, $controller) ->
|
||||
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
|
||||
|
||||
$scope.ShippingMethods = ShippingMethods
|
||||
$scope.name = "shipping"
|
||||
$scope.nextPanel = "payment"
|
||||
|
||||
$scope.summary = ->
|
||||
[$scope.Checkout.shippingMethod()?.name]
|
||||
|
||||
$timeout $scope.onTimeout
|
||||
@@ -25,8 +25,6 @@ angular.module('Darkswarm').controller "OrderCycleChangeCtrl", ($scope, $rootSco
|
||||
Cart.reloadFinalisedLineItems()
|
||||
ChangeableOrdersAlert.reload()
|
||||
$rootScope.$broadcast 'orderCycleSelected'
|
||||
event = new CustomEvent('orderCycleSelected')
|
||||
window.dispatchEvent(event)
|
||||
|
||||
$scope.closesInLessThan3Months = () ->
|
||||
moment().diff(moment(OrderCycle.orders_close_at(), "YYYY-MM-DD HH:mm:SS Z"), 'days') > -75
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
angular.module('Darkswarm').controller "PageSelectionCtrl", ($scope, $rootScope, $location) ->
|
||||
$scope.selectedPage = ->
|
||||
# The path looks like `/contact` for the URL `https://ofn.org/shop#/contact`.
|
||||
# We remove the slash at the beginning.
|
||||
page = $location.path()[1..]
|
||||
|
||||
return $scope.whitelist[0] unless page
|
||||
|
||||
# If the path points to an unrelated path like `/login`, stay where we were.
|
||||
return $scope.lastPage unless page in $scope.whitelist
|
||||
|
||||
$scope.lastPage = page
|
||||
page
|
||||
|
||||
$scope.whitelistPages = (pages) ->
|
||||
$scope.whitelist = pages
|
||||
$scope.lastPage = pages[0]
|
||||
|
||||
# when an order cycle is changed, ensure the shop tab is active to save a click
|
||||
$rootScope.$on "orderCycleSelected", ->
|
||||
if $scope.selectedPage() != "shop"
|
||||
$location.path("shop")
|
||||
@@ -7,11 +7,8 @@ angular.module('Darkswarm').controller "RegistrationFormCtrl", ($scope, Registra
|
||||
form.$valid
|
||||
|
||||
$scope.create = (form) ->
|
||||
if ($scope.valid(form))
|
||||
$scope.disableButton()
|
||||
EnterpriseRegistrationService.create($scope.enableButton).then(() ->
|
||||
$scope.enableButton()
|
||||
)
|
||||
$scope.disableButton()
|
||||
EnterpriseRegistrationService.create($scope.enableButton) if $scope.valid(form)
|
||||
|
||||
$scope.update = (nextStep, form) ->
|
||||
EnterpriseRegistrationService.update(nextStep) if $scope.valid(form)
|
||||
|
||||
@@ -17,12 +17,9 @@ angular.module('Darkswarm').directive "ofnFlash", (flash, $timeout, RailsFlashLo
|
||||
|
||||
# Callback when a new flash message is pushed to flash service
|
||||
show = (message, type)=>
|
||||
return unless message
|
||||
# if same message already exists, don't add it again
|
||||
return if $scope.flashes.some((flash) -> flash.message == message)
|
||||
|
||||
$scope.flashes.push({message: message, type: typePairings[type]})
|
||||
$timeout($scope.delete, 10000)
|
||||
if message
|
||||
$scope.flashes.push({message: message, type: typePairings[type]})
|
||||
$timeout($scope.delete, 10000)
|
||||
|
||||
$scope.delete = ->
|
||||
$scope.flashes.shift()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module('Darkswarm').directive "ofnOnHand", (StockQuantity, Messages) ->
|
||||
angular.module('Darkswarm').directive "ofnOnHand", (StockQuantity) ->
|
||||
restrict: 'A'
|
||||
require: "ngModel"
|
||||
scope: true
|
||||
@@ -16,7 +16,7 @@ angular.module('Darkswarm').directive "ofnOnHand", (StockQuantity, Messages) ->
|
||||
ngModel.$parsers.push (viewValue) ->
|
||||
available_quantity = scope.available_quantity()
|
||||
if parseInt(viewValue) > available_quantity
|
||||
Messages.flash({error: t("js.insufficient_stock", {on_hand: available_quantity})})
|
||||
alert t("js.insufficient_stock", {on_hand: available_quantity})
|
||||
viewValue = available_quantity
|
||||
ngModel.$setViewValue viewValue
|
||||
ngModel.$render()
|
||||
|
||||
@@ -2,7 +2,6 @@ angular.module('Darkswarm').factory "EnterpriseModal", ($modal, $rootScope, $htt
|
||||
# Build a modal popup for an enterprise.
|
||||
new class EnterpriseModal
|
||||
open: (enterprise)->
|
||||
return if enterprise.visible == 'hidden'
|
||||
scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise
|
||||
scope.embedded_layout = window.location.search.indexOf("embedded_shopfront=true") != -1
|
||||
|
||||
|
||||
@@ -55,10 +55,6 @@ angular.module('Darkswarm').factory "EnterpriseRegistrationService", ($http, Reg
|
||||
).catch((response) ->
|
||||
Loading.clear()
|
||||
alert(t('failed_to_update_enterprise_unknown'))
|
||||
if response.data.errors.instagram
|
||||
igErr = document.querySelector("#instagram-error")
|
||||
igErr.style.display = 'block'
|
||||
igErr.textContent = response.data.errors.instagram[0]
|
||||
)
|
||||
|
||||
prepare: =>
|
||||
|
||||
@@ -33,9 +33,9 @@ angular.module('Darkswarm').factory 'Products', (OrderCycleResource, OrderCycle,
|
||||
prices = (v.price for v in product.variants)
|
||||
product.price = Math.min.apply(null, prices)
|
||||
product.hasVariants = product.variants?.length > 0
|
||||
product.primaryImage = product.image?.small_url if product.image
|
||||
product.primaryImage = product.images[0]?.small_url if product.images
|
||||
product.primaryImageOrMissing = product.primaryImage || "/noimage/small.png"
|
||||
product.largeImage = product.image?.large_url if product.image
|
||||
product.largeImage = product.images[0]?.large_url if product.images
|
||||
|
||||
dereference: ->
|
||||
for product in @fetched_products
|
||||
|
||||
5
app/assets/javascripts/iehack.js
Normal file
5
app/assets/javascripts/iehack.js
Normal file
@@ -0,0 +1,5 @@
|
||||
$( document ).ready(function() {
|
||||
$("#closeie").click(function() {
|
||||
$("#ie-warning").hide();
|
||||
});
|
||||
})
|
||||
9227
app/assets/javascripts/shared/jquery-1.8.0.js
vendored
Normal file
9227
app/assets/javascripts/shared/jquery-1.8.0.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,11 @@
|
||||
.ofn-drop-down.ofn-drop-down-v2.right#columns-dropdown{ ng: { controller: 'ColumnsDropdownCtrl' } }
|
||||
.ofn-drop-down-label
|
||||
= " #{t('admin.columns')}".html_safe
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
.ofn-drop-down.right#columns-dropdown{ ng: { controller: 'ColumnsDropdownCtrl' } }
|
||||
%span{ :class => 'icon-reorder' }= " #{t('admin.columns')}".html_safe
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
.menu_items
|
||||
.menu_item{ ng: { repeat: "column in columns", click: "toggle(column);" } }
|
||||
%input.redesigned-input{ type: "checkbox", ng: { checked: "column.visible" } }
|
||||
{{ column.name }}
|
||||
%hr
|
||||
%div.menu_item.text-center
|
||||
%input.fullwidth.orange{ type: "button", ng: { value: "saved() ? 'Saved': 'Saving'", show: "saved() || saving", disabled: "saved()" } }
|
||||
%input.fullwidth.red{ type: "button", :value => t('admin.column_save_as_default').html_safe, ng: { show: "!saved() && !saving", click: "saveColumnPreferences(action)"} }
|
||||
%div.menu_item{ ng: { repeat: "column in columns", click: "toggle(column)", class: "{selected: column.visible}" } }
|
||||
%span.check
|
||||
%span.name {{ column.name }}
|
||||
%hr
|
||||
%div.menu_item.text-center
|
||||
%input.fullwidth.orange{ type: "button", ng: { value: "saved() ? 'Saved': 'Saving'", show: "saved() || saving", disabled: "saved()" } }
|
||||
%input.fullwidth.red{ type: "button", :value => t('admin.column_save_as_default').html_safe, ng: { show: "!saved() && !saving", click: "saveColumnPreferences(action)"} }
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.ofn-drop-down
|
||||
%span
|
||||
%i.icon-check
|
||||
{{ 'admin.actions' | t }}
|
||||
%i{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%a.menu_item{ 'ng-repeat' => "link in links", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method || 'get' }}", confirm: "{{link.confirm}}" } }
|
||||
%span
|
||||
%i{ ng: { class: "link.icon" } }
|
||||
%span {{ link.name }}
|
||||
@@ -0,0 +1,14 @@
|
||||
%div
|
||||
.margin-bottom-30.text-center
|
||||
.text-big
|
||||
{{ 'js.admin.modals.terms_and_conditions_warning.title' | t }}
|
||||
.margin-bottom-30
|
||||
%p
|
||||
{{ 'js.admin.modals.terms_and_conditions_warning.message_1' | t }}
|
||||
.margin-bottom-30
|
||||
%p
|
||||
{{ 'js.admin.modals.terms_and_conditions_warning.message_2' | t }}
|
||||
|
||||
.text-center
|
||||
%input.button.red{ type: 'button', value: t('js.admin.modals.close'), ng: { click: 'close()' } }
|
||||
%input.button.red{ type: 'button', value: t('js.admin.modals.continue'), ng: { click: 'continue()' } }
|
||||
@@ -19,7 +19,7 @@
|
||||
.name {{ product.name }}
|
||||
.supplier {{ product.supplier_name }}
|
||||
|
||||
.exchange-product-variant{'ng-repeat' => 'variant in product.variants | visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges | filter:variantSuppliedToOrderCycle as filteredVariants'}
|
||||
.exchange-product-variant{'ng-repeat' => 'variant in product.variants | visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges | filter:variantSuppliedToOrderCycle'}
|
||||
%label
|
||||
%input{ type: 'checkbox', name: 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
|
||||
value: 1,
|
||||
@@ -27,8 +27,5 @@
|
||||
'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
|
||||
'ng-disabled' => '!order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' }
|
||||
{{ variant.label }}
|
||||
|
||||
%em{ 'ng-if' => 'filteredVariants.length === 0' }
|
||||
{{ 'js.admin.panels.exchange_products.no_variants' | t }}
|
||||
|
||||
%div{ 'ng-include' => "'admin/panels/exchange_products_panel_footer.html'" }
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
.exchange-load-all-variants
|
||||
%div
|
||||
{{ 'js.admin.panels.exchange_products.variants_loaded' | t:{ num_of_variants_loaded: enterprises[exchange.enterprise_id].loaded_variants, total_number_of_variants: exchangeTotalVariants(exchange) } }}
|
||||
%em{ 'ng-if': 'enterprises[exchange.enterprise_id].loaded_variants > exchangeTotalVariants(exchange)' }
|
||||
{{ 'js.admin.panels.exchange_products.some_variants_hidden' | t }}
|
||||
%a{ 'ng-click' => 'loadAllExchangeProducts(exchange)', 'ng-show' => 'enterprises[exchange.enterprise_id].last_page_loaded < enterprises[exchange.enterprise_id].num_of_pages' }
|
||||
{{ 'js.admin.panels.exchange_products.load_all_variants' | t }}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
.seven.columns.alpha
|
||||
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage == ''", style: 'StatusMessage.statusMessage.style' } }
|
||||
{{ StatusMessage.statusMessage.text || " " }}
|
||||
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage !== ''" }, style: 'color: #C85136' }
|
||||
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage !== ''" }, style: 'color: #da5354' }
|
||||
{{ StatusMessage.invalidMessage || " " }}
|
||||
.nine.columns.omega.text-right{ ng: { transclude: true } }
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
{{'hubs_delivery' | t}}
|
||||
.row
|
||||
.columns.small-12
|
||||
%a.cta-hub{"ng-href" => "{{::enterprise.path}}#/shop_panel", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}",
|
||||
%a.cta-hub{"ng-href" => "{{::enterprise.path}}#/shop", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}",
|
||||
"ng-class" => "{primary: enterprise.active, secondary: !enterprise.active}",
|
||||
"ng-click" => "$close()",
|
||||
"ofn-change-hub" => "enterprise"}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
.row
|
||||
.columns.small-12
|
||||
%a.cta-hub{"ng-repeat" => "hub in enterprise.hubs | filter:{id: '!'+enterprise.id} | orderBy:'-active'",
|
||||
"ng-href" => "{{::hub.path}}#/shop_panel", "ofn-empties-cart" => "hub",
|
||||
"ng-href" => "{{::hub.path}}#/shop", "ofn-empties-cart" => "hub",
|
||||
"ng-class" => "::{primary: hub.active, secondary: !hub.active}",
|
||||
"ng-click" => "$close()",
|
||||
"ofn-change-hub" => "hub"}
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
%filter-selector{ 'selector-set' => "productPropertySelectors", objects: "[product] | propertiesWithValuesOf" }
|
||||
|
||||
.product-description{"ng-if" => "product.description_html"}
|
||||
%p.text-small{"ng-bind-html" => "::product.description_html", "data-controller" => "add-blank-to-link"}
|
||||
%p.text-small{"ng-bind-html" => "::product.description_html"}
|
||||
|
||||
.columns.small-12.medium-6.large-6.product-img
|
||||
%img{"ng-src" => "{{::product.largeImage}}", "ng-if" => "::product.largeImage"}
|
||||
%img.placeholder{ src: Spree::Image.default_image_url(:large), "ng-if" => "::!product.largeImage"}
|
||||
%img.placeholder{ src: "/noimage/large.png", "ng-if" => "::!product.largeImage"}
|
||||
|
||||
%ng-include{src: "'partials/close.html'"}
|
||||
|
||||
@@ -1 +1 @@
|
||||
.question-mark-icon{"ng-class" => "{open: tt_isOpen}", type: 'button'}
|
||||
%button.question-mark-icon{"ng-class" => "{open: tt_isOpen}", type: 'button'}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ScopedChannel < ApplicationCable::Channel
|
||||
class << self
|
||||
def for_id(id)
|
||||
"ScopedChannel:#{id}"
|
||||
end
|
||||
end
|
||||
|
||||
def subscribed
|
||||
stream_from "ScopedChannel:#{params[:id]}"
|
||||
end
|
||||
end
|
||||
@@ -1,13 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SessionChannel < ApplicationCable::Channel
|
||||
def self.for_request(request)
|
||||
"SessionChannel:#{request.session.id}"
|
||||
end
|
||||
|
||||
def subscribed
|
||||
return reject if current_user.nil?
|
||||
|
||||
stream_from "SessionChannel:#{session_id}"
|
||||
end
|
||||
end
|
||||
@@ -1,34 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ConfirmModalComponent < ModalComponent
|
||||
# @param actions_alignment_class [String] possible classes: 'justify-space-around', 'justify-end'
|
||||
def initialize(
|
||||
id:,
|
||||
reflex: nil,
|
||||
controller: nil,
|
||||
message: nil,
|
||||
confirm_actions: nil,
|
||||
confirm_reflexes: nil,
|
||||
confirm_button_class: :primary,
|
||||
confirm_button_text: I18n.t('js.admin.modals.confirm'),
|
||||
cancel_button_text: I18n.t('js.admin.modals.cancel'),
|
||||
actions_alignment_class: 'justify-space-around'
|
||||
)
|
||||
super(id:, close_button: true)
|
||||
@confirm_actions = confirm_actions
|
||||
@reflex = reflex
|
||||
@confirm_reflexes = confirm_reflexes
|
||||
@controller = controller
|
||||
@message = message
|
||||
@confirm_button_class = confirm_button_class
|
||||
@confirm_button_text = confirm_button_text
|
||||
@cancel_button_text = cancel_button_text
|
||||
@actions_alignment_class = actions_alignment_class
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def close_button_class
|
||||
"secondary"
|
||||
end
|
||||
end
|
||||
@@ -1,10 +0,0 @@
|
||||
%div{ id: @id, "data-controller": "modal #{@controller}", "data-action": "keyup@document->modal#closeIfEscapeKey", "data-#{@controller}-reflex-value": @reflex }
|
||||
.reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" }
|
||||
.reveal-modal.fade.tiny.modal-component{ "data-modal-target": "modal" }
|
||||
= content
|
||||
|
||||
= render @message if @message
|
||||
|
||||
%div{ class: "modal-actions #{@actions_alignment_class}" }
|
||||
%input{ class: "button icon-plus #{close_button_class}", type: 'button', value: @cancel_button_text, "data-action": "click->modal#close" }
|
||||
%input{ id: 'modal-confirm-button', class: "button icon-plus #{@confirm_button_class}", type: 'button', value: @confirm_button_text, "data-action": @confirm_actions, "data-reflex": @confirm_reflexes }
|
||||
@@ -1,22 +0,0 @@
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
|
||||
&.justify-space-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
&.justify-end {
|
||||
justify-content: flex-end;
|
||||
input[type="button"] {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1024px) {
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
input[type="button"] {
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class HelpModalComponent < ModalComponent
|
||||
class HelpModalComponent < ViewComponent::Base
|
||||
def initialize(id:, close_button: true)
|
||||
super(id:, close_button:)
|
||||
@id = id
|
||||
@close_button = close_button
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def close_button_class
|
||||
if namespace == "admin"
|
||||
"red"
|
||||
else
|
||||
"primary"
|
||||
end
|
||||
end
|
||||
|
||||
def close_button?
|
||||
!!@close_button
|
||||
end
|
||||
|
||||
def namespace
|
||||
helpers.controller_path.split("/").first
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%div{ id: @id, "data-controller": "help-modal", "data-action": "keyup@document->help-modal#closeIfEscapeKey" }
|
||||
.reveal-modal-bg.fade{ "data-help-modal-target": "background", "data-action": "click->help-modal#close" }
|
||||
.reveal-modal.fade.small.modal-component{ "data-help-modal-target": "modal" }
|
||||
.reveal-modal.fade.small.help-modal{ "data-help-modal-target": "modal" }
|
||||
= content
|
||||
|
||||
- if close_button?
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.help-modal {
|
||||
visibility: visible;
|
||||
position: fixed;
|
||||
top: 3em;
|
||||
}
|
||||
|
||||
/* prevent arrow on selected admin menu item appearing above modal */
|
||||
body.modal-open #admin-menu li.selected a::after {
|
||||
z-index: 0;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ModalComponent < ViewComponent::Base
|
||||
def initialize(id:, close_button: true, instant: false)
|
||||
@id = id
|
||||
@close_button = close_button
|
||||
@instant = instant
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def close_button_class
|
||||
if namespace == "admin"
|
||||
"red"
|
||||
else
|
||||
"primary"
|
||||
end
|
||||
end
|
||||
|
||||
def close_button?
|
||||
!!@close_button
|
||||
end
|
||||
|
||||
def namespace
|
||||
helpers.controller_path.split("/").first
|
||||
end
|
||||
end
|
||||
@@ -1,8 +0,0 @@
|
||||
%div{ id: @id, "data-controller": "modal", "data-action": "keyup@document->modal#closeIfEscapeKey", "data-modal-instant-value": @instant }
|
||||
.reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" }
|
||||
.reveal-modal.fade.small.modal-component{ "data-modal-target": "modal" }
|
||||
= content
|
||||
|
||||
- if close_button?
|
||||
.text-center
|
||||
%input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.close'), "data-action": "click->modal#close" }
|
||||
@@ -1,24 +0,0 @@
|
||||
// class name 'modal' is already taken by 'custom-alert' and 'custom-confirm'.
|
||||
.modal-component {
|
||||
visibility: visible;
|
||||
position: fixed;
|
||||
top: 3em;
|
||||
&.in {
|
||||
padding: 1.2rem;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
/* prevent arrow on selected admin menu item appearing above modal */
|
||||
body.modal-open #admin-menu li.selected a::after {
|
||||
z-index: 0;
|
||||
}
|
||||
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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user