Compare commits

..

1 Commits

Author SHA1 Message Date
Jean-Baptiste Bellet
46de90155b Add LoginModal as controller + connect action to open login modal
(cherry picked from commit 92cd918e5a)
2022-02-10 13:57:27 +00:00
975 changed files with 21064 additions and 58241 deletions

View File

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

7
.env
View File

@@ -61,3 +61,10 @@ 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"
# Feature toggles
#
# Adding user emails separated by commas will enable them the use of certain features. See
# config/initializers/feature_toggles.rb for details.
#
# BETA_TESTERS="ofn@example.com,superadmin@example.com"

View File

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

View File

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

13
.github/FUNDING.yml vendored
View File

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

View File

@@ -2,7 +2,7 @@
name: Bug report
about: Create a report to help us improve
title: ''
labels: tech debt
labels: ''
assignees: ''
---

View File

@@ -1,7 +1,7 @@
---
name: Release task
about: Track the process of a new release
title: Release v
title: 'Release v'
labels: ''
assignees: ''
@@ -10,6 +10,7 @@ assignees: ''
## Preparation on Thursday
- [ ] Merge pull requests in the [Ready To Go] column
- [ ] Merge [Transifex pull request]
- [ ] Include translations: `tx pull --force`
- [ ] [Draft new release]. Look at previous [releases] for inspiration.
- [ ] Notify [#instance-managers] of user-facing changes.
@@ -17,12 +18,24 @@ assignees: ''
## Testing
- [ ] [Find build] of the release commit and copy it below.
- [ ] Move this issue to Test Ready.
- [ ] Notify `@testers` in [#testing].
- [ ] Test build: <!-- paste build link here, e.g. https://semaphore...builds/1234 -->
- [ ] Move this issue to Test Ready and notify testers.
- [ ] Test: :warning: link to the build of the release commit https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2/branches/master
## Finish on Tuesday
- [ ] Update translations unless content has been removed from config/locales/en.yml between this release draft and current master.
<details><summary>Command line instructions</summary>
<pre>
git checkout master # same version as the release draft
git fetch upstream
git diff upstream/master -- config/locales/en.yml
tx pull --force # if no changes or only additions in the locale
git checkout --detach # if we need to commit new translations
git commit -a -m "Update translations"
git tag vx.y.z # put the release number in here
git push upstream vx.y.z
</pre>
</details>
- [ ] Publish and notify [#global-community]:
> The next release is ready: https://github.com/openfoodfoundation/openfoodnetwork/releases/latest
- [ ] Deploy the new release to all managed instances.
@@ -44,6 +57,5 @@ The full process is described at https://github.com/openfoodfoundation/openfoodn
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A%23%23+User+facing+changes+:eyes:%0A%0A%0A%0A%23%23+Technical+changes+:wrench:%0A%0A
[releases]: https://github.com/openfoodfoundation/openfoodnetwork/releases
[#instance-managers]: https://app.slack.com/client/T02G54U79/CG7NJ966B
[#testing]: https://openfoodnetwork.slack.com/app_redirect?channel=C02TZ6X00
[Find build]: https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2/branches/master
[#global-community]: https://app.slack.com/client/T02G54U79/C59ADD8F2

View File

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

View File

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

View File

@@ -3,22 +3,18 @@ name: Build
on:
workflow_dispatch:
push:
branches-ignore:
- 'dependabot/**'
pull_request:
env:
DISABLE_KNAPSACK: true
TIMEZONE: UTC
COVERAGE: true
RSPEC_RETRY_RETRY_COUNT: 3
RAILS_ENV: test
permissions:
contents: read
jobs:
rspec:
runs-on: ubuntu-20.04
runs-on: ubuntu-18.04
services:
postgres:
image: postgres:10
@@ -37,31 +33,26 @@ jobs:
specs:
- "spec/controllers"
- "spec/models"
- "spec/features/admin/[a-o0-9]*_spec.rb"
- "spec/lib"
- "spec/migrations"
- "spec/serializers"
- "spec/system/admin/[a-o0-9]*"
- "spec/system/admin/[p-z]*"
- "spec/system/consumer/[a-o0-9]*"
- "spec/system/consumer/[p-z]*"
- "spec/system/admin/[a-o0-9]*_spec.rb"
- "spec/system/admin/[p-z]*_spec.rb"
- "spec/system/consumer"
- "engines/*/spec"
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: actions/setup-node@v3
- uses: actions/setup-node@v2
with:
node-version: 16
node-version: '14.15.5'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
@@ -76,7 +67,7 @@ jobs:
- name: Archive failed tests screenshots
if: failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: failed-tests-screenshots
path: tmp/capybara/screenshots/*.png
@@ -84,7 +75,7 @@ jobs:
if-no-files-found: ignore
test-the-rest:
runs-on: ubuntu-20.04
runs-on: ubuntu-18.04
services:
postgres:
image: postgres:10
@@ -99,21 +90,16 @@ jobs:
POSTGRES_USER: ofn
POSTGRES_PASSWORD: f00d
steps:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: actions/setup-node@v3
- uses: actions/setup-node@v2
with:
node-version: 16
node-version: '14.15.5'
- name: Install JS dependencies
run: yarn install --frozen-lockfile

View File

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

View File

@@ -1,51 +0,0 @@
name: 'Mayhem for API'
on: workflow_dispatch
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
test:
permissions:
contents: read # to fetch code (actions/checkout)
security-events: write # to upload SARIF results (github/codeql-action/upload-sarif)
if: ${{ github.repository_owner == 'openfoodfoundation' }}
runs-on: ubuntu-latest
strategy:
fail-fast: true
steps:
- uses: actions/checkout@v3
- run: docker/build
- run: docker-compose up --detach
- run: until curl -f -s http://localhost:3000; do echo "waiting for api server"; sleep 1; done
- run: docker-compose exec -T db psql postgresql://ofn:f00d@localhost:5432/open_food_network_dev --command="update spree_users set spree_api_key='testing' where login='ofn@example.com'"
# equivalent to Flipper.enable(:api_v1)
- run: docker-compose exec -T db psql postgresql://ofn:f00d@localhost:5432/open_food_network_dev --command="insert into flipper_features (key, created_at, updated_at) values ('api_v1', localtimestamp, localtimestamp)"
- run: docker-compose exec -T db psql postgresql://ofn:f00d@localhost:5432/open_food_network_dev --command="insert into flipper_gates (feature_key, key, value, created_at, updated_at) values ('api_v1', 'boolean', 'true', localtimestamp, localtimestamp)"
# Run Mayhem for API
- name: Run Mayhem for API
uses: ForAllSecure/mapi-action@v1
continue-on-error: true
with:
mapi-token: ${{ secrets.MAPI_TOKEN }}
api-url: http://localhost:3000
api-spec: swagger/v1/swagger.yaml
target: openfoodfoundation/openfoodnetwork
duration: 1min
sarif-report: mapi.sarif
html-report: mapi.html
run-args: |
--header-auth
X-Api-Token: testing
# Archive HTML report
- name: Archive Mayhem for API report
uses: actions/upload-artifact@v3
with:
name: mapi-report
path: mapi.html
# Upload SARIF file (only available on public repos or github enterprise)
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: mapi.sarif

4
.gitignore vendored
View File

@@ -15,7 +15,6 @@ db/*.csv
log/*.log
log/*.log.lck
log/*.log.*
/storage
tmp/
.idea/*
\#*
@@ -57,6 +56,3 @@ coverage
/yarn-error.log
yarn-debug.log*
.yarn-integrity
/config/credentials.yml.enc
/config/master.key

View File

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

View File

@@ -1,21 +0,0 @@
# Basically, ignore everythings expect app/webpacker/controllers/*.js and app/webpacker/packs/*.js
*.css
*.scss
*.md
*.yml
*.yaml
*.json
*.html
babel.config.js
postcss.config.js
.storybook/
/app/assets/
/config/
/coverage/
/engines/
/public/
/spec/
/tmp/
/vendor/

View File

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

View File

@@ -10,9 +10,6 @@ inherit_from:
# The automatically generated todo list to ignore all current violations.
- .rubocop_todo.yml
# The relaxed style rules as a common starting point which we can refine.
- .rubocop_relaxed_styleguide.yml
# Our Open Food Network style guide. If you want to see all violations,
# then use only that configuration:
#

View File

@@ -1,153 +0,0 @@
# Relaxed.Ruby.Style
## Version 2.5
Style/Alias:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylealias
Style/AsciiComments:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styleasciicomments
Style/BeginBlock:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylebeginblock
Style/BlockDelimiters:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styleblockdelimiters
Style/CommentAnnotation:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylecommentannotation
Style/Documentation:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styledocumentation
Layout/DotPosition:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#layoutdotposition
Style/DoubleNegation:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styledoublenegation
Style/EndBlock:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styleendblock
Style/FormatString:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styleformatstring
Style/IfUnlessModifier:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styleifunlessmodifier
Style/Lambda:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylelambda
Style/ModuleFunction:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylemodulefunction
Style/MultilineBlockChain:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylemultilineblockchain
Style/NegatedIf:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylenegatedif
Style/NegatedWhile:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylenegatedwhile
Style/NumericPredicate:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylenumericpredicate
Style/ParallelAssignment:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styleparallelassignment
Style/PercentLiteralDelimiters:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylepercentliteraldelimiters
Style/PerlBackrefs:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styleperlbackrefs
Style/Semicolon:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylesemicolon
Style/SignalException:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylesignalexception
Style/SingleLineBlockParams:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylesinglelineblockparams
Style/SingleLineMethods:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylesinglelinemethods
Layout/SpaceBeforeBlockBraces:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#layoutspacebeforeblockbraces
Layout/SpaceInsideParens:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#layoutspaceinsideparens
Style/SpecialGlobalVars:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylespecialglobalvars
Style/StringLiterals:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylestringliterals
Style/TrailingCommaInArguments:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styletrailingcommainarguments
Style/TrailingCommaInArrayLiteral:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styletrailingcommainarrayliteral
Style/TrailingCommaInHashLiteral:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#styletrailingcommainhashliteral
Style/SymbolArray:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylesymbolarray
Style/WhileUntilModifier:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylewhileuntilmodifier
Style/WordArray:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#stylewordarray
Lint/AmbiguousRegexpLiteral:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#lintambiguousregexpliteral
Lint/AssignmentInCondition:
Enabled: false
StyleGuide: https://relaxed.ruby.style/#lintassignmentincondition
Layout/LineLength:
Enabled: false
Metrics:
Enabled: false

7
.rubocop_specs.yml Normal file
View File

@@ -0,0 +1,7 @@
inherit_from:
- .rubocop.yml
# This rubocop config file is only used for specs
# Here we allow specs to be 300 lines long
Metrics/ModuleLength:
Max: 300

View File

@@ -19,36 +19,6 @@ AllCops:
#
# Cop settings that have been agreed upon by the OFN community
Metrics:
Enabled: true
Metrics/BlockLength:
AllowedMethods: [
"class_eval",
"collection",
"context",
"delete",
"describe",
"feature",
"get",
"it",
"member",
"namespace",
"path",
"post",
"put",
"resource",
"resources",
"scenario",
"shared_examples",
"xdescribe",
]
Rails/ApplicationRecord:
Exclude:
# Migrations should not contain application code:
- "db/migrate/*.rb"
Rails/SkipsModelValidations:
AllowedMethods:
- "touch"
@@ -76,7 +46,6 @@ Layout/MultilineMethodCallIndentation:
EnforcedStyle: indented
Layout/LineLength:
Enabled: true
Max: 100
Lint/RaiseException:
@@ -85,11 +54,6 @@ Lint/RaiseException:
Lint/StructNewOverride:
Enabled: true
Naming/VariableNumber:
AllowedIdentifiers:
- street_address_1
- street_address_2
Bundler/DuplicatedGem:
Enabled: false
@@ -111,20 +75,156 @@ Lint/UselessAssignment:
Exclude:
- spec/**/*
Lint/MissingSuper:
Exclude:
- 'app/components/**/*'
## Relaxed.Ruby.Style SETTINGS
#
# These styles are a starting point for the conversation around conventions
# They should be removed or tweaked and moved above as decisions are made
# NOTE: Cops which did not fail at the time of writing were removed
Layout/DotPosition:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#styledotposition
Layout/SpaceBeforeBlockBraces:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylespacebeforeblockbraces
Layout/SpaceInsideParens:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylespaceinsideparens
Style/Alias:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylealias
Style/BlockDelimiters:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#styleblockdelimiters
Style/CommentAnnotation:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylecommentannotation
Style/DoubleNegation:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#styledoublenegation
Style/FormatString:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#styleformatstring
Style/HashEachMethods:
Enabled: false
Style/HashTransformKeys:
Enabled: false
Style/HashTransformValues:
Enabled: false
Style/IfUnlessModifier:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#styleifunlessmodifier
Style/Lambda:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylelambda
Style/MultilineBlockChain:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylemultilineblockchain
Style/NegatedIf:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylenegatedif
Style/NegatedWhile:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylenegatedwhile
Style/ParallelAssignment:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#styleparallelassignment
Style/PercentLiteralDelimiters:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylepercentliteraldelimiters
Style/Semicolon:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylesemicolon
Style/SingleLineMethods:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylesinglelinemethods
Style/TrailingCommaInArguments:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#styletrailingcommainarguments
Style/TrailingCommaInArrayLiteral:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#styletrailingcommainliteral
Style/TrailingCommaInHashLiteral:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#styletrailingcommainliteral
Style/WordArray:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#stylewordarray
Style/SymbolArray:
Enabled: false
StyleGuide: https://rubocop.readthedocs.io/en/latest/cops_style/#stylesymbolarray
Lint/AmbiguousRegexpLiteral:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#lintambiguousregexpliteral
Lint/AssignmentInCondition:
Enabled: false
StyleGuide: http://relaxed.ruby.style/#lintassignmentincondition
Metrics/AbcSize:
Max: 30 # default 17
Max: 15
Metrics/BlockLength:
Max: 25
IgnoredMethods: [
"class_eval",
"collection",
"context",
"describe",
"feature",
"it",
"member",
"namespace",
"resource",
"resources",
"scenario"
]
Metrics/BlockNesting:
Max: 3
Metrics/ClassLength:
Max: 100
Metrics/ModuleLength:
Max: 100
Metrics/CyclomaticComplexity:
Max: 6
Metrics/MethodLength:
Enabled: true
Max: 25 # default 10
Max: 10
Metrics/ParameterLists:
Max: 5
Metrics/PerceivedComplexity:
Enabled: true
Max: 14 # default 8
Max: 7
Naming/PredicateName:
Enabled: false

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -23,9 +23,7 @@ RUN apt-get update && apt-get install -y \
imagemagick \
unzip \
libjemalloc-dev \
libssl-dev \
ca-certificates \
gnupg
libssl-dev
# Setup ENV variables
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:$PATH
@@ -42,18 +40,20 @@ RUN git clone --depth 1 https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
git clone --depth 1 https://github.com/rbenv/ruby-build.git ${RBENV_ROOT}/plugins/ruby-build && \
echo 'eval "$(rbenv init -)"' >> /etc/profile.d/rbenv.sh && \
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install $(cat .ruby-version) && \
rbenv global $(cat .ruby-version)
rbenv global $(cat .ruby-version) && \
gem install bundler --version=1.17.3
# Install Postgres
RUN sh -c "echo 'deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main' >> /etc/apt/sources.list.d/pgdg.list" && \
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/trusted.gpg.d/apt.postgresql.org.gpg >/dev/null && \
RUN sh -c "echo 'deb https://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main' > /etc/apt/sources.list.d/pgdg.list" && \
wget --quiet -O - https://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | apt-key add - && \
apt-get update && \
apt-get install -yqq --no-install-recommends postgresql-client-10 libpq-dev
apt-get install -yqq --no-install-recommends postgresql-client-9.5 libpq-dev
# 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 node & yarn
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && \
apt-get install -y nodejs yarn
# Install Chrome
RUN wget --quiet -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
@@ -69,9 +69,6 @@ RUN wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.z
# Copy code and install app dependencies
COPY . /usr/src/app/
# Install Bundler
RUN ./script/install-bundler
# Install front-end dependencies
RUN yarn install

View File

@@ -60,7 +60,7 @@ If the script succeeds you're ready to start developing. If not, take a look at
Now, your dreams of spinning up a development server can be realised:
foreman start
bundle exec rails server
Go to [http://localhost:3000](http://localhost:3000) to play around!
@@ -68,8 +68,6 @@ To login as the default user, use:
email: ofn@example.com
password: ofn123
Seee [Locale and sample data] about loading data.
### Testing
@@ -131,4 +129,3 @@ If these commands succeed, you should be able to [continue the setup process](#g
[rubocop]: https://rubocop.readthedocs.io/en/latest/
[karma]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Karma
[slack-dev]: https://openfoodnetwork.slack.com/messages/C2GQ45KNU
[Locale and sample data]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Locale-and-sample-data

46
Gemfile
View File

@@ -8,11 +8,6 @@ gem 'dotenv-rails', require: 'dotenv/rails-now' # Load ENV vars before other gem
gem 'rails', '>= 6.1.4'
# Active Storage
gem "active_storage_validations"
gem "aws-sdk-s3", require: false
gem "image_processing"
gem 'activemerchant', '>= 1.78.0'
gem 'rexml'
gem 'angular-rails-templates', '>= 0.3.0'
@@ -29,7 +24,6 @@ gem 'rails_safe_tasks', '~> 1.0'
gem "activerecord-import"
gem "db2fog", github: "openfoodfoundation/db2fog", branch: "rails-6"
gem "fog-aws", "~> 2.0" # db2fog does not support v3
gem "mime-types" # required by fog
gem "valid_email2"
@@ -63,17 +57,10 @@ gem 'devise-token_authenticatable'
gem 'jwt', '~> 2.3'
gem 'oauth2', '~> 1.4.7' # Used for Stripe Connect
gem 'jsonapi-serializer'
gem 'pagy', '~> 5.1'
gem 'rswag-api'
gem 'rswag-ui'
gem 'gitlab-omniauth-openid-connect', require: 'omniauth_openid_connect'
gem 'openid_connect', '~> 1.3'
gem 'omniauth-rails_csrf_protection'
gem 'angularjs-rails', '1.8.0'
gem 'aws-sdk', '1.67.0'
gem 'bugsnag'
gem 'haml'
gem 'redcarpet'
@@ -90,6 +77,7 @@ gem 'bootsnap', require: false
gem 'geocoder'
gem 'gmaps4rails'
gem 'mimemagic', '> 0.3.5'
gem 'paperclip', '~> 3.4.1'
gem 'paper_trail', '~> 12.1.0'
gem 'rack-rewrite'
gem 'rack-ssl', require: 'rack/ssl'
@@ -101,15 +89,14 @@ gem 'redis', '>= 4.0', require: ['redis', 'redis/connection/hiredis']
gem 'sidekiq'
gem 'sidekiq-scheduler'
gem "cable_ready", "5.0.0.pre9"
gem "stimulus_reflex", "3.5.0.pre9"
gem "cable_ready", "5.0.0.pre3"
gem 'combine_pdf'
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
gem 'immigrant'
gem 'roo'
gem 'roo', github: "roo-rb/roo" # master is currently needed for Ruby 3.x (awaiting new release)
gem 'spreadsheet_architect'
gem 'whenever', require: false
@@ -120,6 +107,8 @@ gem 'coffee-rails', '~> 5.0.0'
gem 'mini_racer', '0.4.0'
gem 'uglifier', '>= 1.0.3'
gem 'angular_rails_csrf'
gem 'jquery-rails', '4.4.0'
@@ -134,7 +123,7 @@ gem 'flipper'
gem 'flipper-active_record'
gem 'flipper-ui'
gem "view_component"
gem "view_component", require: "view_component/engine"
group :production, :staging do
gem 'ddtrace'
@@ -143,6 +132,8 @@ group :production, :staging do
end
group :test, :development do
# Pretty printed test output
gem 'awesome_print'
gem 'bullet'
gem 'capybara'
gem 'cuprite'
@@ -150,40 +141,41 @@ group :test, :development do
gem "factory_bot_rails", '6.2.0', require: false
gem 'fuubar', '~> 2.5.1'
gem 'json_spec', '~> 1.1.4'
gem 'knapsack', require: false
gem 'knapsack'
gem 'letter_opener', '>= 1.4.1'
gem 'rspec-rails', ">= 3.5.2"
gem 'rspec-retry', require: false
gem 'rswag-specs'
gem 'rspec-retry'
gem 'rswag'
gem 'selenium-webdriver'
gem 'shoulda-matchers'
gem 'timecop'
gem 'debug', '>= 1.0.0'
gem 'webdrivers'
end
group :test do
gem 'byebug'
gem 'pdf-reader'
gem 'rails-controller-testing'
gem 'simplecov', require: false
gem 'test-prof', require: false
gem 'vcr', require: false
gem 'webmock', require: false
gem 'test-prof'
gem 'webmock'
# See spec/spec_helper.rb for instructions
# gem 'perftools.rb'
end
group :development do
gem 'debugger-linecache'
gem 'rails-erd'
gem 'foreman'
gem 'listen'
gem 'pry', '~> 0.13.0'
gem 'pry-byebug', '~> 3.9.0'
gem 'rubocop'
gem 'rubocop-rails'
gem 'spring'
gem 'spring-commands-rspec'
gem 'web-console'
gem "view_component_storybook"
gem "view_component_storybook", require: "view_component/storybook/engine"
gem 'rack-mini-profiler', '< 3.0.0'
end

View File

@@ -24,6 +24,14 @@ GIT
sass-rails
thor (>= 0.14)
GIT
remote: https://github.com/roo-rb/roo.git
revision: 709464c77623be2bc09b2103405d90ded7604a75
specs:
roo (2.8.3)
nokogiri (~> 1)
rubyzip (>= 1.3.0, < 3.0.0)
PATH
remote: engines/catalog
specs:
@@ -51,67 +59,62 @@ GEM
remote: https://rubygems.org/
specs:
Ascii85 (1.1.0)
actioncable (6.1.7)
actionpack (= 6.1.7)
activesupport (= 6.1.7)
actioncable (6.1.4.4)
actionpack (= 6.1.4.4)
activesupport (= 6.1.4.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.1.7)
actionpack (= 6.1.7)
activejob (= 6.1.7)
activerecord (= 6.1.7)
activestorage (= 6.1.7)
activesupport (= 6.1.7)
actionmailbox (6.1.4.4)
actionpack (= 6.1.4.4)
activejob (= 6.1.4.4)
activerecord (= 6.1.4.4)
activestorage (= 6.1.4.4)
activesupport (= 6.1.4.4)
mail (>= 2.7.1)
actionmailer (6.1.7)
actionpack (= 6.1.7)
actionview (= 6.1.7)
activejob (= 6.1.7)
activesupport (= 6.1.7)
actionmailer (6.1.4.4)
actionpack (= 6.1.4.4)
actionview (= 6.1.4.4)
activejob (= 6.1.4.4)
activesupport (= 6.1.4.4)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.1.7)
actionview (= 6.1.7)
activesupport (= 6.1.7)
actionpack (6.1.4.4)
actionview (= 6.1.4.4)
activesupport (= 6.1.4.4)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionpack-action_caching (1.2.2)
actionpack (>= 4.0.0)
actiontext (6.1.7)
actionpack (= 6.1.7)
activerecord (= 6.1.7)
activestorage (= 6.1.7)
activesupport (= 6.1.7)
actiontext (6.1.4.4)
actionpack (= 6.1.4.4)
activerecord (= 6.1.4.4)
activestorage (= 6.1.4.4)
activesupport (= 6.1.4.4)
nokogiri (>= 1.8.5)
actionview (6.1.7)
activesupport (= 6.1.7)
actionview (6.1.4.4)
activesupport (= 6.1.4.4)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_model_serializers (0.8.4)
activemodel (>= 3.0)
active_storage_validations (1.0.3)
activejob (>= 5.2.0)
activemodel (>= 5.2.0)
activestorage (>= 5.2.0)
activesupport (>= 5.2.0)
activejob (6.1.7)
activesupport (= 6.1.7)
activejob (6.1.4.4)
activesupport (= 6.1.4.4)
globalid (>= 0.3.6)
activemerchant (1.123.0)
activesupport (>= 4.2)
builder (>= 2.1.2, < 4.0.0)
i18n (>= 0.6.9)
nokogiri (~> 1.4)
activemodel (6.1.7)
activesupport (= 6.1.7)
activerecord (6.1.7)
activemodel (= 6.1.7)
activesupport (= 6.1.7)
activerecord-import (1.4.1)
activemodel (6.1.4.4)
activesupport (= 6.1.4.4)
activerecord (6.1.4.4)
activemodel (= 6.1.4.4)
activesupport (= 6.1.4.4)
activerecord-import (1.3.0)
activerecord (>= 4.2)
activerecord-postgresql-adapter (0.0.1)
pg
@@ -121,14 +124,14 @@ GEM
multi_json (~> 1.11, >= 1.11.2)
rack (>= 2.0.8, < 3)
railties (>= 5.2.4.1)
activestorage (6.1.7)
actionpack (= 6.1.7)
activejob (= 6.1.7)
activerecord (= 6.1.7)
activesupport (= 6.1.7)
marcel (~> 1.0)
activestorage (6.1.4.4)
actionpack (= 6.1.4.4)
activejob (= 6.1.4.4)
activerecord (= 6.1.4.4)
activesupport (= 6.1.4.4)
marcel (~> 1.0.0)
mini_mime (>= 1.1.0)
activesupport (6.1.7)
activesupport (6.1.4.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@@ -138,66 +141,48 @@ GEM
activerecord (>= 5.0, < 6.2)
acts_as_list (1.0.4)
activerecord (>= 4.2)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
aes_key_wrap (1.1.0)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
afm (0.2.2)
angular-rails-templates (1.2.0)
railties (>= 5.0, < 7.1)
angular-rails-templates (1.1.0)
railties (>= 4.2, < 7)
sprockets (>= 3.0, < 5)
sprockets-rails
tilt
angular_rails_csrf (5.0.0)
railties (>= 3, < 8)
angular_rails_csrf (4.5.0)
railties (>= 3, < 7)
angularjs-file-upload-rails (2.4.1)
angularjs-rails (1.8.0)
arel-helpers (2.14.0)
activerecord (>= 3.1.0, < 8)
arel-helpers (2.12.1)
activerecord (>= 3.1.0, < 7)
ast (2.4.2)
attr_required (1.0.1)
awesome_nested_set (3.5.0)
activerecord (>= 4.0.0, < 7.1)
aws-eventstream (1.2.0)
aws-partitions (1.651.0)
aws-sdk-core (3.165.1)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.59.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.117.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.2)
aws-eventstream (~> 1, >= 1.0.2)
awesome_nested_set (3.4.0)
activerecord (>= 4.0.0, < 7.0)
awesome_print (1.9.2)
aws-sdk (1.67.0)
aws-sdk-v1 (= 1.67.0)
aws-sdk-v1 (1.67.0)
json (~> 1.4)
nokogiri (~> 1)
axlsx_styler (1.1.0)
activesupport (>= 3.1)
caxlsx (>= 2.0.2)
bcrypt (3.1.18)
bcrypt (3.1.16)
bigdecimal (3.0.2)
bindata (2.4.12)
bindex (0.8.1)
bootsnap (1.13.0)
bootsnap (1.10.1)
msgpack (~> 1.2)
bugsnag (6.24.2)
bugsnag (6.24.1)
concurrent-ruby (~> 1.0)
builder (3.2.4)
bullet (7.0.3)
bullet (6.1.5)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
cable_ready (5.0.0.pre9)
actioncable (>= 5.2)
actionpack (>= 5.2)
actionview (>= 5.2)
activerecord (>= 5.2)
activesupport (>= 5.2)
railties (>= 5.2)
byebug (11.1.3)
cable_ready (5.0.0.pre3)
rails (>= 5.2)
thread-local (>= 1.1.0)
cancancan (1.15.0)
capybara (3.38.0)
capybara (3.36.0)
addressable
matrix
mini_mime (>= 0.1.3)
@@ -211,9 +196,12 @@ GEM
marcel (~> 1.0)
nokogiri (~> 1.10, >= 1.10.4)
rubyzip (>= 1.3.0, < 3)
choice (0.2.0)
childprocess (4.1.0)
chronic (0.10.2)
climate_control (0.2.0)
cliver (0.3.2)
cocaine (0.5.8)
climate_control (>= 0.0.3, < 1.0)
coderay (1.1.3)
coffee-rails (5.0.0)
coffee-script (>= 2.2.0)
@@ -222,15 +210,14 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
combine_pdf (1.0.22)
matrix
combine_pdf (1.0.21)
ruby-rc4 (>= 0.1.5)
concurrent-ruby (1.1.10)
connection_pool (2.3.0)
concurrent-ruby (1.1.9)
connection_pool (2.2.5)
crack (0.4.5)
rexml
crass (1.0.6)
css_parser (1.11.0)
css_parser (1.9.0)
addressable
cuprite (0.13)
capybara (>= 2.1, < 4)
@@ -245,11 +232,8 @@ GEM
debase-ruby_core_source (= 0.10.12)
msgpack
debase-ruby_core_source (0.10.12)
debug (1.6.3)
irb (>= 1.3.6)
reline (>= 0.3.1)
debugger-linecache (1.2.0)
devise (4.8.1)
devise (4.8.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
@@ -257,20 +241,21 @@ GEM
warden (~> 1.2.3)
devise-encryptable (0.2.0)
devise (>= 2.1.0)
devise-i18n (1.10.2)
devise-i18n (1.10.0)
devise (>= 4.8.0)
devise-token_authenticatable (1.1.0)
devise (>= 4.0.0, < 5.0.0)
diff-lcs (1.5.0)
diff-lcs (1.4.4)
digest (3.1.0)
docile (1.4.0)
dotenv (2.8.1)
dotenv-rails (2.8.1)
dotenv (= 2.8.1)
dotenv (2.7.6)
dotenv-rails (2.7.6)
dotenv (= 2.7.6)
railties (>= 3.2)
dry-inflector (0.2.1)
erubi (1.11.0)
et-orbi (1.2.7)
e2mmap (0.1.0)
erubi (1.10.0)
et-orbi (1.2.4)
tzinfo
excon (0.81.0)
execjs (2.7.0)
@@ -279,18 +264,21 @@ GEM
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faraday (2.6.0)
faraday-net_http (>= 2.0, < 3.1)
faraday (1.4.1)
faraday-excon (~> 1.1)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.1)
multipart-post (>= 1.2, < 3)
ruby2_keywords (>= 0.0.4)
faraday-follow_redirects (0.3.0)
faraday (>= 1, < 3)
faraday-net_http (3.0.1)
faraday-excon (1.1.0)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.1.0)
ferrum (0.11)
addressable (~> 2.5)
cliver (~> 0.3)
concurrent-ruby (~> 1.1)
websocket-driver (>= 0.6, < 0.8)
ffaker (2.21.0)
ffaker (2.20.0)
ffi (1.15.5)
flipper (0.20.4)
flipper-active_record (0.20.4)
@@ -318,21 +306,17 @@ GEM
nokogiri (>= 1.5.11, < 2.0.0)
foreman (0.87.2)
formatador (0.2.5)
fugit (1.7.1)
et-orbi (~> 1, >= 1.2.7)
fugit (1.4.5)
et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.4)
fuubar (2.5.1)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
geocoder (1.8.1)
gitlab-omniauth-openid-connect (0.10.0)
addressable (~> 2.7)
omniauth (>= 1.9, < 3)
openid_connect (~> 1.2)
geocoder (1.7.0)
globalid (1.0.0)
activesupport (>= 5.0)
gmaps4rails (2.1.2)
good_migrations (0.2.1)
good_migrations (0.1.0)
activerecord (>= 3.1)
railties (>= 3.1)
haml (5.2.2)
@@ -340,57 +324,40 @@ GEM
tilt
hashdiff (1.0.1)
hashery (2.1.2)
hashie (5.0.0)
highline (2.0.3)
hiredis (0.6.3)
htmlentities (4.3.4)
httpclient (2.8.3)
i18n (1.12.0)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
i18n-js (3.9.2)
i18n-js (3.9.0)
i18n (>= 0.6.6)
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
immigrant (0.3.6)
activerecord (>= 3.0)
io-console (0.5.11)
ipaddress (0.8.3)
irb (1.4.2)
reline (>= 0.3.0)
jmespath (1.6.1)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
json (2.6.2)
json-jwt (1.16.0)
activesupport (>= 4.2)
aes_key_wrap
bindata
faraday (~> 2.0)
faraday-follow_redirects
json-schema (3.0.0)
addressable (>= 2.8)
json (2.6.1)
json-schema (2.8.1)
addressable (>= 2.4)
json_spec (1.1.5)
multi_json (~> 1.0)
rspec (>= 2.0, < 4.0)
jsonapi-serializer (2.2.0)
activesupport (>= 4.2)
jwt (2.5.0)
jwt (2.3.0)
knapsack (4.0.0)
rake
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.8.1)
launchy (>= 2.2, < 3)
letter_opener (1.7.0)
launchy (~> 2.2)
libv8-node (15.14.0.1)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.19.0)
loofah (2.13.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@@ -398,74 +365,57 @@ GEM
marcel (1.0.2)
matrix (0.4.2)
method_source (1.0.0)
mime-types (3.4.1)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2021.0225)
mimemagic (0.4.3)
nokogiri (~> 1)
rake
mini_magick (4.11.0)
mini_mime (1.1.2)
mini_portile2 (2.8.0)
mini_portile2 (2.7.1)
mini_racer (0.4.0)
libv8-node (~> 15.14.0.0)
minitest (5.16.3)
minitest (5.15.0)
monetize (1.12.0)
money (~> 6.12)
money (6.16.0)
i18n (>= 0.6.4, <= 2)
msgpack (1.5.4)
msgpack (1.4.2)
multi_json (1.15.0)
multi_xml (0.6.0)
net-protocol (0.1.3)
timeout
net-smtp (0.3.2)
net-protocol
multipart-post (2.1.1)
nio4r (2.5.8)
nokogiri (1.13.9)
mini_portile2 (~> 2.8.0)
nokogiri (1.13.1)
mini_portile2 (~> 2.7.0)
racc (~> 1.4)
oauth2 (1.4.11)
faraday (>= 0.17.3, < 3.0)
oauth2 (1.4.7)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 4)
omniauth (2.1.0)
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
openid_connect (1.4.2)
activemodel
attr_required (>= 1.0.0)
json-jwt (>= 1.15.0)
net-smtp
rack-oauth2 (~> 1.21)
swd (~> 1.3)
tzinfo
validate_email
validate_url
webfinger (~> 1.2)
rack (>= 1.2, < 3)
orm_adapter (0.5.0)
pagy (5.10.1)
activesupport
pagy (5.1.2)
paper_trail (12.1.0)
activerecord (>= 5.2)
request_store (~> 1.1)
parallel (1.22.1)
paranoia (2.6.0)
activerecord (>= 5.1, < 7.1)
parser (3.1.2.1)
paperclip (3.4.2)
activemodel (>= 3.0.0)
activerecord (>= 3.0.0)
activesupport (>= 3.0.0)
cocaine (~> 0.5.0)
mime-types
parallel (1.21.0)
paranoia (2.4.3)
activerecord (>= 4.0, < 6.2)
parser (3.1.0.0)
ast (~> 2.4.1)
paypal-sdk-core (0.3.4)
multi_json (~> 1.0)
xml-simple
paypal-sdk-merchant (1.117.2)
paypal-sdk-core (~> 0.3.0)
pdf-reader (2.11.0)
pdf-reader (2.5.0)
Ascii85 (~> 1.0)
afm (~> 0.2.1)
hashery (~> 2.0)
@@ -476,20 +426,17 @@ GEM
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (5.0.0)
puma (5.6.5)
pry-byebug (3.9.0)
byebug (~> 11.0)
pry (~> 0.13.0)
public_suffix (4.0.6)
puma (5.5.2)
nio4r (~> 2.0)
raabro (1.4.0)
racc (1.6.0)
rack (2.2.4)
rack-mini-profiler (2.3.4)
rack (2.2.3)
rack-mini-profiler (2.3.3)
rack (>= 1.2.0)
rack-oauth2 (1.21.3)
activesupport
attr_required
httpclient
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
rack-protection (2.1.0)
rack
rack-proxy (0.7.0)
@@ -497,23 +444,23 @@ GEM
rack-rewrite (1.5.1)
rack-ssl (1.4.1)
rack
rack-test (2.0.2)
rack (>= 1.3)
rack-timeout (0.6.3)
rails (6.1.7)
actioncable (= 6.1.7)
actionmailbox (= 6.1.7)
actionmailer (= 6.1.7)
actionpack (= 6.1.7)
actiontext (= 6.1.7)
actionview (= 6.1.7)
activejob (= 6.1.7)
activemodel (= 6.1.7)
activerecord (= 6.1.7)
activestorage (= 6.1.7)
activesupport (= 6.1.7)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rack-timeout (0.6.0)
rails (6.1.4.4)
actioncable (= 6.1.4.4)
actionmailbox (= 6.1.4.4)
actionmailer (= 6.1.4.4)
actionpack (= 6.1.4.4)
actiontext (= 6.1.4.4)
actionview (= 6.1.4.4)
activejob (= 6.1.4.4)
activemodel (= 6.1.4.4)
activerecord (= 6.1.4.4)
activestorage (= 6.1.4.4)
activesupport (= 6.1.4.4)
bundler (>= 1.15.0)
railties (= 6.1.7)
railties (= 6.1.4.4)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
@@ -522,22 +469,17 @@ GEM
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-erd (1.7.2)
activerecord (>= 4.2)
activesupport (>= 4.2)
choice (~> 0.2.0)
ruby-graphviz (~> 1.2)
rails-html-sanitizer (1.4.3)
rails-html-sanitizer (1.4.2)
loofah (~> 2.3)
rails-i18n (7.0.5)
rails-i18n (7.0.1)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
rails_safe_tasks (1.0.0)
railties (6.1.7)
actionpack (= 6.1.7)
activesupport (= 6.1.7)
railties (6.1.4.4)
actionpack (= 6.1.4.4)
activesupport (= 6.1.4.4)
method_source
rake (>= 12.2)
rake (>= 0.13)
thor (~> 1.0)
rainbow (3.1.1)
rake (13.0.6)
@@ -549,42 +491,37 @@ GEM
rb-inotify (0.10.1)
ffi (~> 1.0)
redcarpet (3.5.1)
redis (4.8.0)
regexp_parser (2.6.0)
reline (0.3.1)
io-console (~> 0.5)
redis (4.5.1)
regexp_parser (2.2.0)
request_store (1.5.0)
rack (>= 1.4)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rexml (3.2.5)
roadie (5.0.1)
roadie (4.0.0)
css_parser (~> 1.4)
nokogiri (~> 1.8)
roadie-rails (3.0.0)
railties (>= 5.1, < 7.1)
roadie (~> 5.0)
roadie-rails (2.2.0)
railties (>= 5.1, < 6.2)
roadie (>= 3.1, < 5.0)
rodf (1.1.1)
builder (>= 3.0)
dry-inflector (~> 0.1)
rubyzip (>= 1.0)
roo (2.9.0)
nokogiri (~> 1)
rubyzip (>= 1.3.0, < 3.0.0)
rspec (3.10.0)
rspec-core (~> 3.10.0)
rspec-expectations (~> 3.10.0)
rspec-mocks (~> 3.10.0)
rspec-core (3.10.2)
rspec-core (3.10.1)
rspec-support (~> 3.10.0)
rspec-expectations (3.10.2)
rspec-expectations (3.10.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0)
rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0)
rspec-rails (5.1.2)
rspec-rails (5.0.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
railties (>= 5.2)
@@ -594,42 +531,40 @@ GEM
rspec-support (~> 3.10)
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.10.3)
rswag-api (2.7.0)
railties (>= 3.1, < 7.1)
rswag-specs (2.7.0)
activesupport (>= 3.1, < 7.1)
json-schema (>= 2.2, < 4.0)
railties (>= 3.1, < 7.1)
rspec-core (>= 2.14)
rswag-ui (2.7.0)
actionpack (>= 3.1, < 7.1)
railties (>= 3.1, < 7.1)
rubocop (1.38.0)
json (~> 2.3)
rspec-support (3.10.2)
rswag (2.4.0)
rswag-api (= 2.4.0)
rswag-specs (= 2.4.0)
rswag-ui (= 2.4.0)
rswag-api (2.4.0)
railties (>= 3.1, < 7.0)
rswag-specs (2.4.0)
activesupport (>= 3.1, < 7.0)
json-schema (~> 2.2)
railties (>= 3.1, < 7.0)
rswag-ui (2.4.0)
actionpack (>= 3.1, < 7.0)
railties (>= 3.1, < 7.0)
rubocop (1.22.2)
parallel (~> 1.10)
parser (>= 3.1.2.1)
parser (>= 3.0.0.0)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.23.0, < 2.0)
rexml
rubocop-ast (>= 1.12.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.23.0)
parser (>= 3.1.1.0)
rubocop-rails (2.17.2)
rubocop-ast (1.15.1)
parser (>= 3.0.1.1)
rubocop-rails (2.13.2)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
ruby-graphviz (1.2.5)
rexml
rubocop (>= 1.7.0, < 2.0)
ruby-progressbar (1.11.0)
ruby-rc4 (0.1.5)
ruby-vips (2.1.4)
ffi (~> 1.12)
ruby2_keywords (0.0.5)
ruby2_keywords (0.0.4)
rubyzip (2.3.2)
rufus-scheduler (3.8.2)
rufus-scheduler (3.7.0)
fugit (~> 1.1, >= 1.1.6)
sass (3.4.25)
sass-rails (5.0.8)
@@ -639,17 +574,23 @@ GEM
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sd_notify (0.1.1)
selenium-webdriver (4.0.3)
childprocess (>= 0.5, < 5.0)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2)
semantic_range (3.0.0)
shoulda-matchers (5.2.0)
shoulda-matchers (5.0.0)
activesupport (>= 5.2.0)
sidekiq (6.5.8)
connection_pool (>= 2.2.5, < 3)
sidekiq (6.3.1)
connection_pool (>= 2.2.2)
rack (~> 2.0)
redis (>= 4.5.0, < 5)
sidekiq-scheduler (4.0.3)
redis (>= 4.2.0)
sidekiq-scheduler (3.1.0)
e2mmap
redis (>= 3, < 5)
rufus-scheduler (~> 3.2)
sidekiq (>= 4, < 7)
sidekiq (>= 3)
thwait
tilt (>= 1.4.0)
simplecov (0.21.2)
docile (~> 1.1)
@@ -661,7 +602,7 @@ GEM
axlsx_styler (>= 1.0.0, < 2)
caxlsx (>= 2.0.2, < 4)
rodf (>= 1.0.0, < 2)
spring (4.1.0)
spring (3.0.0)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
sprockets (3.7.2)
@@ -678,63 +619,45 @@ GEM
state_machines-activerecord (0.8.0)
activerecord (>= 5.1)
state_machines-activemodel (>= 0.8.0)
stimulus_reflex (3.5.0.pre9)
actioncable (>= 5.2)
actionpack (>= 5.2)
actionview (>= 5.2)
activesupport (>= 5.2)
cable_ready (>= 5.0.0.pre9)
nokogiri
rack
railties (>= 5.2)
redis
stringex (2.8.5)
stripe (7.1.0)
swd (1.3.0)
activesupport (>= 3)
attr_required (>= 0.0.5)
httpclient (>= 2.4)
stripe (5.42.0)
temple (0.8.2)
test-prof (1.0.11)
test-unit (3.5.5)
test-prof (1.0.7)
test-unit (3.5.0)
power_assert
thor (1.2.1)
thread-local (1.1.0)
tilt (2.0.11)
timecop (0.9.5)
timeout (0.3.0)
thwait (0.2.0)
e2mmap
tilt (2.0.10)
timecop (0.9.4)
ttfunk (1.7.0)
tzinfo (2.0.5)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
unicode-display_width (2.3.0)
uniform_notifier (1.16.0)
valid_email2 (4.0.4)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (2.1.0)
uniform_notifier (1.14.2)
valid_email2 (4.0.0)
activemodel (>= 3.2)
mail (~> 2.5)
validate_email (0.1.6)
activemodel (>= 3.0)
mail (>= 2.2.5)
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
vcr (6.1.0)
view_component (2.75.0)
view_component (2.41.0)
activesupport (>= 5.0.0, < 8.0)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
view_component_storybook (0.11.1)
view_component_storybook (0.10.1)
view_component (>= 2.36)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.2.0)
web-console (4.1.0)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webfinger (1.2.0)
activesupport
httpclient (>= 2.4)
webmock (3.18.1)
webdrivers (5.0.0)
nokogiri (~> 1.6)
rubyzip (>= 1.3.0)
selenium-webdriver (~> 4.0)
webmock (3.14.0)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -748,13 +671,13 @@ GEM
websocket-extensions (0.1.5)
whenever (1.0.0)
chronic (>= 0.6.3)
wicked_pdf (2.6.3)
wicked_pdf (2.1.0)
activesupport
wkhtmltopdf-binary (0.12.6.5)
xml-simple (1.1.8)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.4)
zeitwerk (2.5.3)
PLATFORMS
ruby
@@ -762,7 +685,6 @@ PLATFORMS
DEPENDENCIES
actionpack-action_caching
active_model_serializers (= 0.8.4)
active_storage_validations
activemerchant (>= 1.78.0)
activerecord-import
activerecord-postgresql-adapter
@@ -775,12 +697,14 @@ DEPENDENCIES
angularjs-rails (= 1.8.0)
arel-helpers (~> 2.12)
awesome_nested_set
aws-sdk-s3
awesome_print
aws-sdk (= 1.67.0)
bigdecimal (= 3.0.2)
bootsnap
bugsnag
bullet
cable_ready (= 5.0.0.pre9)
byebug
cable_ready (= 5.0.0.pre3)
cancancan (~> 1.15.0)
capybara
catalog!
@@ -790,7 +714,6 @@ DEPENDENCIES
database_cleaner
db2fog!
ddtrace
debug (>= 1.0.0)
debugger-linecache
devise
devise-encryptable
@@ -808,7 +731,6 @@ DEPENDENCIES
foreman
fuubar (~> 2.5.1)
geocoder
gitlab-omniauth-openid-connect
gmaps4rails
good_migrations
haml
@@ -816,33 +738,30 @@ DEPENDENCIES
hiredis
i18n
i18n-js (~> 3.9.0)
image_processing
immigrant
jquery-rails (= 4.4.0)
jquery-ui-rails (~> 4.2)
json
json_spec (~> 1.1.4)
jsonapi-serializer
jwt (~> 2.3)
knapsack
letter_opener (>= 1.4.1)
listen
mime-types
mimemagic (> 0.3.5)
mini_racer (= 0.4.0)
monetize (~> 1.11)
oauth2 (~> 1.4.7)
ofn-qz!
omniauth-rails_csrf_protection
openid_connect (~> 1.3)
order_management!
pagy (~> 5.1)
paper_trail (~> 12.1.0)
paperclip (~> 3.4.1)
paranoia (~> 2.4)
paypal-sdk-merchant (= 1.117.2)
pdf-reader
pg (~> 1.2.3)
pry (~> 0.13.0)
pry-byebug (~> 3.9.0)
puma
rack-mini-profiler (< 3.0.0)
rack-rewrite
@@ -850,7 +769,6 @@ DEPENDENCIES
rack-timeout
rails (>= 6.1.4)
rails-controller-testing
rails-erd
rails-i18n
rails_safe_tasks (~> 1.0)
ransack (= 2.4.2)
@@ -859,16 +777,15 @@ DEPENDENCIES
responders
rexml
roadie-rails
roo
roo!
rspec-rails (>= 3.5.2)
rspec-retry
rswag-api
rswag-specs
rswag-ui
rswag
rubocop
rubocop-rails
sd_notify
select2-rails!
selenium-webdriver
shoulda-matchers
sidekiq
sidekiq-scheduler
@@ -877,18 +794,18 @@ DEPENDENCIES
spring
spring-commands-rspec
state_machines-activerecord
stimulus_reflex (= 3.5.0.pre9)
stringex (~> 2.8.5)
stripe
test-prof
test-unit (~> 3.5)
timecop
uglifier (>= 1.0.3)
valid_email2
vcr
view_component
view_component_storybook
web!
web-console
webdrivers
webmock
webpacker (~> 5)
whenever

View File

@@ -39,7 +39,7 @@ We use [BrowserStack](https://www.browserstack.com/) as a manual testing tool. B
## Licence
Copyright (c) 2012 - 2022 Open Food Foundation, released under the AGPL licence.
Copyright (c) 2012 - 2021 Open Food Foundation, released under the AGPL licence.
[survey]: https://docs.google.com/a/eaterprises.com.au/forms/d/1zxR5vSiU9CigJ9cEaC8-eJLgYid8CR8er7PPH9Mc-30/edit#
[slack-invite]: https://join.slack.com/t/openfoodnetwork/shared_invite/zt-9sjkjdlu-r02kUMP1zbrTgUhZhYPF~A

View File

@@ -22,6 +22,24 @@
//= require angular-rails-templates
//= require lodash.underscore.js
// datetimepicker (fil, nb)
//= require flatpickr/dist/flatpickr.min
//= require flatpickr/dist/l10n/ar
//= require flatpickr/dist/l10n/cat
//= require flatpickr/dist/l10n/cy
//= require flatpickr/dist/l10n/de
//= require flatpickr/dist/l10n/es
//= require flatpickr/dist/l10n/fr
//= require flatpickr/dist/l10n/it
//= require flatpickr/dist/l10n/nl
//= require flatpickr/dist/l10n/pl
//= require flatpickr/dist/l10n/pt
//= require flatpickr/dist/l10n/ru
//= require flatpickr/dist/l10n/sv
//= require flatpickr/dist/l10n/tr
//= require shortcut-buttons-flatpickr/dist/shortcut-buttons-flatpickr.min
//= require flatpickr/dist/plugins/labelPlugin/labelPlugin
// spree
//= require admin/spree/spree
//= require admin/spree/spree-select2
@@ -87,8 +105,6 @@
//= require moment/locale/tr.js
//= require moment/locale/pl.js
//= require js-big-decimal/dist/web/js-big-decimal.min.js
// foundation
//= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js

View File

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

View File

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

View File

@@ -0,0 +1,6 @@
angular.module("ofn.admin").directive "select2NoSearch", ($timeout) ->
restrict: 'CA'
link: (scope, element, attrs) ->
$timeout ->
element.select2
minimumResultsForSearch: Infinity

View File

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

View File

@@ -9,9 +9,9 @@ angular.module("admin.enterpriseFees").directive 'spreeEnsureCalculatorPreferenc
settings = element.parent().parent().find('div.calculator-settings')
if value == orig_calculator_type
settings.show()
settings.find('input, select').prop 'disabled', false
settings.find('input').prop 'disabled', false
else
settings.hide()
settings.find('input, select').prop 'disabled', true
settings.find('input').prop 'disabled', true
return
return

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,8 +5,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.confirmDelete = true
$scope.startDate = moment().startOf('day').subtract(7, 'days').format('YYYY-MM-DD')
$scope.endDate = moment().startOf('day').format('YYYY-MM-DD')
$scope.previousDates = { startDate: $scope.startDate, endDate: $scope.endDate }
$scope.datesChangedOnCancelEvent = false
$scope.bulkActions = [ { name: t("admin.orders.bulk_management.actions_delete"), callback: 'deleteLineItems' } ]
$scope.selectedUnitsProduct = {}
$scope.selectedUnitsVariant = {}
@@ -27,28 +25,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.resetFilters()
$scope.refreshData()
$scope.$watchCollection "[startDate, endDate]", (newValues, oldValues) ->
if newValues != oldValues && !$scope.datesChangedOnCancelEvent
state = $scope.refreshData()
if (state == "cancel")
$scope.datesChangedOnCancelEvent = true
$scope.cancelDateChange()
$scope.cancelDateChange = ->
# Reset the date filters to the previous values
$scope.startDate = $scope.previousDates.startDate
$scope.endDate = $scope.previousDates.endDate
# throw a flatpickr:change event to change the date back in the datepicker
event = new CustomEvent('flatpickr:change', {
detail: {
startDate: $scope.previousDates.startDate,
endDate: $scope.previousDates.endDate
}
})
window.dispatchEvent(event)
$timeout ->
$scope.datesChangedOnCancelEvent = false
$scope.refreshData = ->
unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == ''
$scope.setOrderCycleDateRange()
@@ -58,8 +34,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
return unless moment($scope.formattedStartDate).isValid() and moment($scope.formattedEndDate).isValid()
return "cancel" unless $scope.confirmRefresh()
$scope.loadOrders()
$scope.loadLineItems()
@@ -67,11 +41,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.loadAssociatedData()
$scope.dereferenceLoadedData()
$timeout ->
# update the previous dates with the current ones since loading was successful
$scope.previousDates.startDate = $scope.startDate
$scope.previousDates.endDate = $scope.endDate
$scope.setOrderCycleDateRange = ->
start_date = OrderCycles.byID[$scope.orderCycleFilter].orders_open_at
@@ -135,45 +104,15 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
else
StatusMessage.display 'failure', t "unsaved_changes_error"
$scope.cancelOrder = (order, sendEmailCancellation) ->
return $http(
method: 'GET'
url: "/admin/orders/#{order.number}/fire?e=cancel&send_cancellation_email=#{sendEmailCancellation}")
$scope.deleteLineItem = (lineItem) ->
if lineItem.order.item_count == 1
ofnCancelOrderAlert((confirm, sendEmailCancellation) ->
if confirm
$scope.cancelOrder(lineItem.order, sendEmailCancellation).then(->
$scope.refreshData()
)
else
$scope.refreshData()
, "js.admin.deleting_item_will_cancel_order")
else if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete
LineItems.delete(lineItem, () -> $scope.refreshData())
if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete
LineItems.delete lineItem
$scope.deleteLineItems = (lineItems) ->
lineItemsToDelete = lineItems.filter (item) -> item.checked
willCancelOrders = false
itemsPerOrder = new Map()
for item in lineItemsToDelete
{ order } = item
if itemsPerOrder.has(order)
itemsPerOrder.get(order).push(item)
else
itemsPerOrder.set(order, [item])
willCancelOrders = true if (order.item_count == itemsPerOrder.get(order).length)
if willCancelOrders
ofnCancelOrderAlert((confirm, sendEmailCancellation) ->
if confirm
itemsPerOrder.forEach (items, order) =>
if order.item_count == items.length
$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")
$scope.deleteLineItems = (lineItemsToDelete) ->
existingState = $scope.confirmDelete
$scope.confirmDelete = false
$scope.deleteLineItem lineItem for lineItem in lineItemsToDelete when lineItem.checked
$scope.confirmDelete = existingState
$scope.allBoxesChecked = ->
checkedCount = $scope.filteredLineItems.reduce (count,lineItem) ->
@@ -189,71 +128,37 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.selectedUnitsProduct = unitsProduct
$scope.selectedUnitsVariant = unitsVariant
$scope.getLineItemScale = (lineItem) ->
if lineItem.units_product && lineItem.units_variant && (lineItem.units_product.variant_unit == "weight" || lineItem.units_product.variant_unit == "volume")
lineItem.units_product.variant_unit_scale
else
1
$scope.sumUnitValues = ->
sum = $scope.filteredLineItems?.reduce (sum, lineItem) ->
if lineItem.units_product.variant_unit == "items"
sum + lineItem.quantity
else
sum + $scope.roundToThreeDecimals(lineItem.final_weight_volume / $scope.getLineItemScale(lineItem))
sum = $scope.filteredLineItems?.reduce (sum,lineItem) ->
sum + lineItem.final_weight_volume
, 0
$scope.sumMaxUnitValues = ->
sum = $scope.filteredLineItems?.reduce (sum,lineItem) ->
if lineItem.units_product.variant_unit == "items"
sum + lineItem.max_quantity
else
sum + lineItem.max_quantity * $scope.roundToThreeDecimals(lineItem.units_variant.unit_value / $scope.getLineItemScale(lineItem))
sum + lineItem.max_quantity * lineItem.units_variant.unit_value
, 0
$scope.roundToThreeDecimals = (value) ->
Math.round(value * 1000) / 1000
$scope.allFinalWeightVolumesPresent = ->
for i,lineItem of $scope.filteredLineItems
return false if !lineItem.hasOwnProperty('final_weight_volume') || !(lineItem.final_weight_volume > 0)
true
$scope.getScale = (unitsProduct, unitsVariant) ->
if unitsProduct.hasOwnProperty("variant_unit") && (unitsProduct.variant_unit == "weight" || unitsProduct.variant_unit == "volume")
unitsProduct.variant_unit_scale
else if unitsProduct.hasOwnProperty("variant_unit") && unitsProduct.variant_unit == "items"
1
else
null
$scope.getFormattedValueWithUnitName = (value, unitsProduct, unitsVariant, scale) ->
unit_name = VariantUnitManager.getUnitName(scale, unitsProduct.variant_unit)
$scope.roundToThreeDecimals(value) + " " + unit_name
$scope.getGroupBySizeFormattedValueWithUnitName = (value, unitsProduct, unitsVariant) ->
scale = $scope.getScale(unitsProduct, unitsVariant)
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
''
# How is this different to OptionValueNamer#name?
# Should it be extracted to that class or VariantUnitManager?
$scope.formattedValueWithUnitName = (value, unitsProduct, unitsVariant) ->
scale = $scope.getScale(unitsProduct, unitsVariant)
if scale
$scope.getFormattedValueWithUnitName(value, unitsProduct, unitsVariant, scale)
else
# A Units Variant is an API object which holds unit properies of a variant
if unitsProduct.hasOwnProperty("variant_unit") && (unitsProduct.variant_unit == "weight" || unitsProduct.variant_unit == "volume") && value > 0
scale = VariantUnitManager.getScale(value, unitsProduct.variant_unit)
Math.round(value/scale * 1000)/1000 + " " + VariantUnitManager.getUnitName(scale, unitsProduct.variant_unit)
else
''
$scope.fulfilled = (sumOfUnitValues) ->
# A Units Variant is an API object which holds unit properies of a variant
if $scope.selectedUnitsProduct.hasOwnProperty("group_buy_unit_size")&& $scope.selectedUnitsProduct.group_buy_unit_size > 0 &&
$scope.selectedUnitsProduct.hasOwnProperty("variant_unit")
if $scope.selectedUnitsProduct.variant_unit == "weight" || $scope.selectedUnitsProduct.variant_unit == "volume"
scale = $scope.selectedUnitsProduct.variant_unit_scale
sumOfUnitValues = sumOfUnitValues * scale unless scale == 28.35 || scale == 453.6
$scope.roundToThreeDecimals(sumOfUnitValues / $scope.selectedUnitsProduct.group_buy_unit_size)
if $scope.selectedUnitsProduct.hasOwnProperty("group_buy_unit_size") && $scope.selectedUnitsProduct.group_buy_unit_size > 0 &&
$scope.selectedUnitsProduct.hasOwnProperty("variant_unit") &&
( $scope.selectedUnitsProduct.variant_unit == "weight" || $scope.selectedUnitsProduct.variant_unit == "volume" )
Math.round( sumOfUnitValues / $scope.selectedUnitsProduct.group_buy_unit_size * 1000)/1000
else
''

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $timeout, StatusMessage, Panels, Enterprise) ->
angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $timeout, StatusMessage, Panels) ->
OrderCycleResource = $resource '/admin/order_cycles/:action_name/:order_cycle_id.json', {}, {
'index': { method: 'GET', isArray: true}
'new' : { method: 'GET', params: { action_name: "new" } }
@@ -50,14 +50,7 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $
(callback || angular.noop)()
addDistributor: (new_distributor_id, callback) ->
exchange = { enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: [] }
if (Enterprise.hub_enterprises.length == 1)
editable = this.order_cycle["editable_variants_for_outgoing_exchanges"][new_distributor_id] || []
variants = this.incomingExchangesVariants()
for variant in variants when variant in editable
exchange.variants[variant] = true
this.order_cycle.outgoing_exchanges.push(exchange)
this.order_cycle.outgoing_exchanges.push({ enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: [] })
$timeout ->
(callback || angular.noop)()
@@ -93,9 +86,9 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $
variant_ids
participatingEnterpriseIds: ->
suppliers = (parseInt(exchange.enterprise_id) for exchange in this.order_cycle.incoming_exchanges)
distributors = (parseInt(exchange.enterprise_id) for exchange in this.order_cycle.outgoing_exchanges)
Array.from(new Set([suppliers..., distributors...]))
suppliers = (exchange.enterprise_id for exchange in this.order_cycle.incoming_exchanges)
distributors = (exchange.enterprise_id for exchange in this.order_cycle.outgoing_exchanges)
jQuery.unique(suppliers.concat(distributors)).sort()
exchangesByDirection: (direction) ->
if direction == 'incoming'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,21 +27,26 @@ angular.module("admin.products").factory "VariantUnitManager", (availableUnits)
1000.0:
name: 'kL'
system: 'metric'
'items':
1:
name: 'items'
@variantUnitOptions: ->
available = availableUnits.split(",")
options = for unit_type, _ of @units
for scale in @unitScales(unit_type, available)
name = @getUnitName(scale, unit_type)
["#{I18n.t(unit_type)} (#{name})", @getUnitWithScale(unit_type, scale)]
["#{I18n.t(unit_type)} (#{name})", "#{unit_type}_#{scale}"]
options.push [[I18n.t('items'), 'items']]
options = [].concat options...
@getUnitWithScale: (unit_type, scale) ->
"#{unit_type}_#{scale}"
@getScale: (value, unitType) ->
scaledValue = null
validScales = []
unitScales = VariantUnitManager.unitScales(unitType)
validScales.unshift scale for scale in unitScales when value/scale >= 1
if validScales.length > 0
validScales[0]
else
unitScales[0]
@getUnitName: (scale, unitType) ->
if @units[unitType][scale]

View File

@@ -43,7 +43,7 @@ angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCy
angular.extend(@byID[orderCycle.id], orderCycle)
angular.extend(@pristineByID[orderCycle.id], orderCycle)
form.$setPristine() if form?
StatusMessage.display('success', t('order_cycles_bulk_update_notice'))
StatusMessage.display('success', "Order cycles have been updated.")
, (response) =>
if response.data.errors?
StatusMessage.display('failure', response.data.errors[0])

View File

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

View File

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

View File

@@ -30,14 +30,3 @@ angular.module("admin.side_menu")
for item in @items when item.name is name
return item
null
redirect_function: (elementID , href) =>
window.addEventListener 'load', ->
element = document.getElementById(elementID)
if !element
return
element.addEventListener 'click', ->
window.location.replace(href)
return
return

View File

@@ -7,11 +7,11 @@ $(function() {
if (calculator_select.val() === original_calc_type) {
$('div.calculator-settings').show();
$('.calculator-settings-warning').hide();
$('.calculator-settings').find('input,textarea,select').prop("disabled", false);
$('.calculator-settings').find('input,textarea').prop("disabled", false);
} else {
$('div.calculator-settings').hide();
$('.calculator-settings-warning').show();
$('.calculator-settings').find('input,textarea,select').prop("disabled", true);
$('.calculator-settings').find('input,textarea').prop("disabled", true);
}
});
})

View File

@@ -0,0 +1,17 @@
var update_state = function(region) {
var country = $('span#' + region + 'country .select2').select2('val');
var state_select = $('span#' + region + 'state select.select2');
$.get(Spree.routes.states_search + "?country_id=" + country, function(states) {
state_select.html('');
var states_with_blank = [{name: '', id: ''}].concat(states);
$.each(states_with_blank, function(pos,state) {
var opt = $(document.createElement('option'))
.attr('value', state.id)
.html(state.name);
state_select.append(opt);
});
state_select.prop("disabled", false).show();
state_select.select2();
})
};

View File

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

View File

@@ -4,7 +4,6 @@ $(document).ready(function() {
initAlert()
initConfirm()
initCancelOrder()
if ($('#variant_autocomplete_template').length > 0) {
window.variantTemplate = Handlebars.compile($('#variant_autocomplete_template').text());
@@ -53,12 +52,12 @@ $(document).ready(function() {
}
toggleItemEdit();
adjustItems(shipment_number, variant_id, quantity, true);
adjustItems(shipment_number, variant_id, quantity);
return false;
}
$('a.save-item').click(handle_save_click);
handle_delete_click = function(elementSelector, restock_item){
handle_delete_click = function(elementSelector){
var del = $(elementSelector);
del.hide()
var shipment_number = del.data('shipment-number');
@@ -66,65 +65,38 @@ $(document).ready(function() {
toggleItemEdit();
adjustItems(shipment_number, variant_id, 0, restock_item);
adjustItems(shipment_number, variant_id, 0);
}
$('a.delete-item').click((event) => {
try {
var del = $('a.delete-item');
var shipment_number = del.data('shipment-number');
var variant_id = del.data('variant-id');
var shipment = _.findWhere(shipments, {number: shipment_number + ''});
var inventory_units = _.where(shipment.inventory_units, {variant_id: variant_id});
if (inventory_units.length !== shipment.inventory_units.length) {
ofnConfirm((reStockItem) => {
handle_delete_click('#custom-confirm', reStockItem);
});
} else {
adjustItems(shipment_number, variant_id, 0);
}
} catch (e) {
}
ofnConfirm(() => {
handle_delete_click('#custom-confirm');
});
});
}
});
adjustItems = function(shipment_number, variant_id, quantity, restock_item){
adjustItems = function(shipment_number, variant_id, quantity){
var shipment = _.findWhere(shipments, {number: shipment_number + ''});
var inventory_units = _.where(shipment.inventory_units, {variant_id: variant_id});
if (quantity === 0 && inventory_units.length === shipment.inventory_units.length) {
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_item) => {
if (confirm) {
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);
window.location.href = redirectTo.toString();
});
}
});
if (quantity == 0 && inventory_units.length == shipment.inventory_units.length) {
ofnAlert(t("js.admin.orders.cannot_remove_last_item"));
return;
}
doAdjustItems(shipment_number, variant_id, quantity, inventory_units, restock_item, () => {
window.location.reload();
});
}
doAdjustItems = function(shipment_number, variant_id, quantity, inventory_units, restock_item, callback) {
var url = Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number;
var new_quantity = 0;
var data = { variant_id: variant_id };
if (inventory_units.length < quantity) {
url += "/add";
new_quantity = (quantity - inventory_units.length);
} else if (inventory_units.length > quantity) {
url += "/remove"
new_quantity = (inventory_units.length - quantity);
data.restock_item = restock_item;
}
url += '.json';
data.quantity = new_quantity;
if (new_quantity == 0) {
ofnAlert(t("js.admin.orders.quantity_unchanged"));
@@ -132,35 +104,17 @@ doAdjustItems = function(shipment_number, variant_id, quantity, inventory_units,
$.ajax({
type: "PUT",
url: Spree.url(url),
data: data
data: { variant_id: variant_id, quantity: new_quantity }
}).done(function( msg ) {
callback();
window.location.reload();
});
}
}
toggleTrackingEdit = function(){
var link = $(this);
var parent_node = link.parents('tbody')
let input = parent_node.find('#tracking')[0]
parent_node.find('tr.edit-tracking').toggle();
// Set focus on input and
// put cursor at it's end
input.focus()
input.setSelectionRange(-1, -1)
parent_node.find('tr.show-tracking').toggle();
}
toggleNoteEdit = function(){
var link = $(this);
var parent_node = link.parents('tbody')
let input = parent_node.find('#note')[0]
parent_node.find('tr.edit-note').toggle();
// Set focus on input and
// put cursor at it's end
input.focus()
input.setSelectionRange(-1, -1)
parent_node.find('tr.show-note').toggle();
link.parents('tbody').find('tr.edit-tracking').toggle();
link.parents('tbody').find('tr.show-tracking').toggle();
}
toggleMethodEdit = function(){
@@ -204,7 +158,7 @@ addVariantFromStockLocation = function() {
});
}else{
//add to existing shipment
adjustItems(shipment.number, variant_id, quantity, true);
adjustItems(shipment.number, variant_id, quantity);
}
return 1
}
@@ -227,54 +181,8 @@ ofnAlert = function(message) {
$('#custom-alert').show();
}
ofnCancelOrderAlert = function(callback, i18nKey) {
if (i18nKey == undefined) {
i18nKey = "js.admin.orders.cancel_the_order_html";
}
$('#custom-confirm .message').html(
` ${t(i18nKey)}
<div class="form">
<input type="checkbox" name="send_cancellation_email" value="1" id="send_cancellation_email" checked="true" />
<label for="send_cancellation_email">${t("js.admin.orders.cancel_the_order_send_cancelation_email")}</label><br />
<input type="checkbox" name="restock_items" id="restock_items" checked="checked"/>
<label for="restock_items">${t("js.admin.orders.restock_items")}</label>
</div>`);
$('#custom-confirm button.confirm').unbind( "click" ).click(() => {
$('#custom-confirm').hide();
callback(true, $('#send_cancellation_email').is(':checked'), $('#restock_items').is(':checked'));
});
$('#custom-confirm button.cancel').click(() => {
$('#custom-confirm').hide();
callback(false)
});
$('#custom-confirm').show();
}
ofnConfirm = function(callback) {
$('#custom-confirm .message').html(
` ${t("are_you_sure")}
<div class="form">
<input type="checkbox" name="restock_items" id="restock_items" checked="checked"/>
<label for="restock_items">${t("js.admin.orders.restock_item")}</label>
</div>`);
$('#custom-confirm').data($(event.target).data());
$('#custom-confirm button.confirm').click(() => {
callback($('#restock_items').is(':checked'));
});
$('#custom-confirm button.confirm').click(callback);
$('#custom-confirm').show();
}
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;
});
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,20 @@
angular.module("admin.utils").directive 'helpModal', ($rootScope, $compile, $templateCache, $window, DialogDefaults) ->
restrict: 'C'
scope:
template: '@'
link: (scope, element, attr) ->
# Compile modal template
template = $compile($templateCache.get(scope.template))(scope)
# Load Dialog Options
template.dialog(DialogDefaults)
# Link opening of dialog to click event on element
element.bind 'click', (e) ->
template.dialog('open')
$rootScope.$evalAsync()
scope.close = ->
template.dialog('close')
$rootScope.$evalAsync()
return

View File

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

View File

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

View File

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

View File

@@ -67,7 +67,7 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $
page: page || $scope.page,
per_page: $scope.per_page,
'q[name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont]': $scope.query,
'q[with_properties][]': $scope.activeProperties,
'q[properties_id_or_supplier_properties_id_in_any][]': $scope.activeProperties,
'q[primary_taxon_id_in_any][]': $scope.activeTaxons
}

View File

@@ -1,4 +1,4 @@
angular.module('Darkswarm').controller "ShopVariantCtrl", ($scope, $modal, Cart, Shopfront) ->
angular.module('Darkswarm').controller "ShopVariantCtrl", ($scope, $modal, Cart) ->
$scope.updateCart = (line_item) ->
Cart.adjust($scope.variant.line_item)
@@ -69,6 +69,3 @@ angular.module('Darkswarm').controller "ShopVariantCtrl", ($scope, $modal, Cart,
$scope.addBulk = (quantity) ->
$scope.add(quantity)
$modal.open(templateUrl: "bulk_buy_modal.html", scope: $scope, windowClass: "product-bulk-modal")
$scope.displayRemainingInStock = ->
Shopfront.shopfront.preferred_product_low_stock_display && $scope.available() <= 3 && !$scope.variant.line_item.quantity

View File

@@ -0,0 +1,13 @@
angular.module('Darkswarm').directive "helpModal", ($modal, $compile, $templateCache)->
restrict: 'A'
scope:
helpText: "@helpModal"
link: (scope, elem, attrs, ctrl)->
compiled = $compile($templateCache.get('help-modal.html'))(scope)
elem.on "click", =>
$modal.open(controller: ctrl, template: compiled, scope: scope, windowClass: 'help-modal small')
scope.$on "$destroy", ->
elem.off("click")

View File

@@ -3,7 +3,7 @@ angular.module('Darkswarm').directive 'mapSearch', ($timeout, Search) ->
restrict: 'E'
require: ['^uiGmapGoogleMap', 'ngModel']
replace: true
template: '<input id="pac-input" ng-model="query" placeholder="' + t('location_placeholder') + '" onfocus="this.select()"></input>'
template: '<input id="pac-input" ng-model="query" placeholder="' + t('location_placeholder') + '"></input>'
scope: {}
controller: ($scope) ->

View File

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

View File

@@ -0,0 +1,7 @@
%div
.margin-bottom-30
%p
{{ 'js.admin.modals.business_address_info.message' | t }}
.text-center
%input.button.red.icon-plus{ type: 'button', value: '{{ "js.admin.modals.got_it" | t }}', ng: { click: 'close()' } }

View File

@@ -0,0 +1,19 @@
#invite-manager-modal{ng: {app: 'admin.enterprises', controller: 'enterpriseCtrl'}}
.margin-bottom-30.text-center
.text-big
= t('js.admin.modals.invite_title')
%p.alert-box.ok{ng: {show: 'invite_success'}}
{{invite_success}}
%p.alert-box.error{ng: {show: 'invite_errors'}}
{{invite_errors}}
%input#invite_email.fullwidth.margin-bottom-20{ng: {model: 'newUser'}}
.margin-bottom-20.text-center
%button.text-center.margin-top-10{ng: {show: '!invite_success', click: 'inviteManager()'}}
= t('js.admin.modals.invite')
%button.text-center.margin-top-10{ng: {show: 'invite_success', click: 'resetModal(); close()'}}
= t('js.admin.modals.close')

View File

@@ -0,0 +1,25 @@
#tag-rule-help
.margin-bottom-30.text-center
.text-big
{{ 'js.admin.modals.tag_rule_help.title' | t }}
.margin-bottom-30
.text-normal
{{ 'js.admin.modals.tag_rule_help.overview' | t }}
%p
{{ 'js.admin.modals.tag_rule_help.overview_text' | t }}
.margin-bottom-30
.text-normal
{{ 'js.admin.modals.tag_rule_help.by_default_rules' | t }}
%p
{{ 'js.admin.modals.tag_rule_help.by_default_rules_text' | t }}
.margin-bottom-30
.text-normal
{{ 'js.admin.modals.tag_rule_help.customer_tagged_rules' | t }}
%p
{{ 'js.admin.modals.tag_rule_help.customer_tagged_rules_text' | t }}
.text-center
%input.button.red.icon-plus{ type: 'button', value: t('js.admin.modals.got_it'), ng: { click: 'close()' } }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +0,0 @@
%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.help-modal{ "data-help-modal-target": "modal" }
= content
- if close_button?
.text-center
%input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.got_it'), "data-action": "click->help-modal#close" }

View File

@@ -1,10 +0,0 @@
.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;
}

View File

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

View File

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

View File

@@ -1,15 +0,0 @@
# frozen_string_literal: true
class FeatureToggleConstraint
def initialize(feature_name)
@feature = feature_name
end
def matches?(request)
OpenFoodNetwork::FeatureToggle.enabled?(@feature, current_user(request))
end
def current_user(request)
request.env['warden'].user
end
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
class SplitCheckoutConstraint
def matches?(request)
Flipper.enabled? :split_checkout, current_user(request)
end
def current_user(request)
@spree_current_user ||= request.env['warden'].user
end
end

View File

@@ -10,14 +10,14 @@ module Admin
def update
params.each do |name, value|
if value.is_a?(ActionDispatch::Http::UploadedFile)
blob = store_file(value)
update_preference("#{name}_blob_id", blob.id)
else
update_preference(name, value)
if ContentConfig.has_preference?(name) || ContentConfig.has_attachment?(name)
ContentConfig.public_send("#{name}=", value)
end
end
# Save any uploaded images
ContentConfig.save
flash[:success] =
t(:successfully_updated, resource: I18n.t('admin.contents.edit.your_content'))
@@ -26,22 +26,6 @@ module Admin
private
def store_file(attachable)
ActiveStorage::Blob.create_and_upload!(
io: attachable.open,
filename: attachable.original_filename,
content_type: attachable.content_type,
service_name: :local,
identify: false,
)
end
def update_preference(name, value)
return unless ContentConfig.has_preference?(name)
ContentConfig.public_send("#{name}=", value)
end
def preference_sections
[
PreferenceSections::HeaderSection.new,

View File

@@ -99,7 +99,7 @@ module Admin
def customer_params
params.require(:customer).permit(
:enterprise_id, :first_name, :last_name, :email, :code, :tag_list,
:enterprise_id, :name, :email, :code, :tag_list,
ship_address_attributes: PermittedAttributes::Address.attributes,
bill_address_attributes: PermittedAttributes::Address.attributes,
)

View File

@@ -6,7 +6,6 @@ module Admin
class EnterpriseFeesController < Admin::ResourceController
before_action :load_enterprise_fee_set, only: :index
before_action :load_data
before_action :check_enterprise_fee_input, only: [:bulk_update]
def index
@include_calculators = params[:include_calculators].present?
@@ -36,6 +35,13 @@ module Admin
end
def bulk_update
@flat_percent_value = enterprise_fee_bulk_params.dig('collection_attributes', '0', 'calculator_attributes', 'preferred_flat_percent')
unless @flat_percent_value.nil? || Float(@flat_percent_value, exception: false)
flash[:error] = I18n.t(:calculator_preferred_value_error)
return redirect_to redirect_path
end
@enterprise_fee_set = Sets::EnterpriseFeeSet.new(enterprise_fee_bulk_params)
if @enterprise_fee_set.save
@@ -99,25 +105,5 @@ module Admin
]
)
end
def check_enterprise_fee_input
enterprise_fee_bulk_params['collection_attributes'].each do |_, fee_row|
enterprise_fees = fee_row['calculator_attributes']&.slice(
:preferred_flat_percent, :preferred_amount,
:preferred_first_item, :preferred_additional_item,
:preferred_minimal_amount, :preferred_normal_amount,
:preferred_discount_amount, :preferred_per_unit
)
next unless enterprise_fees
enterprise_fees.each do |_, enterprise_amount|
unless enterprise_amount.nil? || Float(enterprise_amount, exception: false)
flash[:error] = I18n.t(:calculator_preferred_value_error)
return redirect_to redirect_path
end
end
end
end
end
end

View File

@@ -7,7 +7,6 @@ require 'open_food_network/order_cycle_permissions'
module Admin
class EnterprisesController < Admin::ResourceController
include GeocodeEnterpriseAddress
include CablecarResponses
# These need to run before #load_resource so that @object is initialised with sanitised values
prepend_before_action :override_owner, only: :create
@@ -45,12 +44,7 @@ module Admin
def edit
@object = Enterprise.where(permalink: params[:id]).
includes(users: [:ship_address, :bill_address]).first
if params[:stimulus]
@enterprise.is_primary_producer = params[:is_primary_producer]
@enterprise.sells = params[:enterprise_sells]
render operations: cable_car.morph("#side_menu", partial("admin/shared/side_menu"))
.morph("#permalink", partial("admin/enterprises/form/permalink"))
end
super
end
def welcome
@@ -240,10 +234,8 @@ module Admin
end
def update_enterprise_notifications
user_id = params[:receives_notifications].to_i
if user_id.positive? && @enterprise.user_ids.include?(user_id)
@enterprise.update_contact(user_id)
if params.key? :receives_notifications
@enterprise.update_contact params[:receives_notifications]
end
end

View File

@@ -19,7 +19,6 @@ module Admin
:enable_invoices?,
:invoice_style2?,
:enable_receipt_printing?,
:enterprise_number_required_on_invoices?,
)
end
end

View File

@@ -1,7 +0,0 @@
# frozen_string_literal: true
module Admin
class OidcSettingsController < Spree::Admin::BaseController
def index; end
end
end

View File

@@ -2,10 +2,10 @@
module Admin
class OrderCyclesController < Admin::ResourceController
include ::OrderCyclesHelper
include OrderCyclesHelper
include PaperTrailLogging
prepend_before_action :set_order_cycle_id, only: [:incoming, :outgoing, :checkout_options]
prepend_before_action :set_order_cycle_id, only: [:incoming, :outgoing]
before_action :load_data_for_index, only: :index
before_action :require_coordinator, only: :new
before_action :remove_protected_attrs, only: [:update]
@@ -64,22 +64,18 @@ module Admin
@order_cycle_form = OrderCycleForm.new(@order_cycle, order_cycle_params, spree_current_user)
if @order_cycle_form.save
update_nil_subscription_line_items_price_estimate(@order_cycle)
respond_to do |format|
flash[:notice] = I18n.t(:order_cycles_update_notice) if params[:reloading] == '1'
format.html { redirect_to_after_update_path }
format.html { redirect_back(fallback_location: root_path) }
format.json { render json: { success: true } }
end
elsif request.format.html?
render :checkout_options
elsif request.format.json?
else
render json: { errors: @order_cycle.errors.full_messages }, status: :unprocessable_entity
end
end
def bulk_update
if order_cycle_set&.save
bulk_update_nil_subscription_line_items_price_estimate
render_as_json @order_cycles,
ams_prefix: 'index',
current_user: spree_current_user,
@@ -90,27 +86,6 @@ module Admin
end
end
def bulk_update_nil_subscription_line_items_price_estimate
@collection.upcoming.each do |order_cycle|
update_nil_subscription_line_items_price_estimate(order_cycle)
end
end
def update_nil_subscription_line_items_price_estimate(order_cycle)
order_cycle.schedules.each do |schedule|
Subscription.where(schedule_id: schedule.id).each do |subscription|
shop = Enterprise.managed_by(spree_current_user).find_by(id: subscription.shop_id)
subscription.subscription_line_items.nil_price_estimate.each do |line_item|
variant = OrderManagement::Subscriptions::
VariantsList.eligible_variants(shop).find_by(id: line_item.variant_id)
fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, order_cycle)
price = variant.price + fee_calculator.indexed_fees_for(variant)
line_item.update_column(:price_estimate, price)
end
end
end
end
def clone
@order_cycle = OrderCycle.find params[:id]
@order_cycle.clone!
@@ -192,16 +167,6 @@ module Admin
end
end
def redirect_to_after_update_path
if params[:context] == "checkout_options" && params[:save]
redirect_to main_app.admin_order_cycle_checkout_options_path(@order_cycle)
elsif params[:context] == "checkout_options" && params[:save_and_back_to_list]
redirect_to main_app.admin_order_cycles_path
else
redirect_back(fallback_location: root_path)
end
end
def require_coordinator
@order_cycle.coordinator =
permitted_coordinating_enterprises_for(@order_cycle).find_by(id: params[:coordinator_id])

View File

@@ -5,23 +5,14 @@ module Admin
include ReportsActions
helper ReportsHelper
before_action :authorize_report, only: [:show]
# Define model class for Can? permissions
def model_class
Admin::ReportsController
end
def index
@reports = reports.select do |report_type, _description|
can? report_type, :report
end
end
before_action :authorize_report
def show
@report = report_class.new(spree_current_user, params, request)
render_report && return if ransack_params.blank?
if report_format.present?
@report = report_class.new(spree_current_user, ransack_params, report_options)
if export_spreadsheet?
export_report
else
render_report
@@ -31,33 +22,33 @@ module Admin
private
def export_report
send_data @report.render_as(report_format, controller: self), filename: report_filename
render report_format.to_sym => @report.public_send("to_#{report_format}"),
:filename => report_filename
end
def render_report
assign_view_data
render "show"
load_form_options
render report_type
end
def assign_view_data
@report_type = report_type
@report_subtypes = report_subtypes
@report_subtype = report_subtype
@report_title = if report_subtype
report_subtype_title
else
I18n.t(:name, scope: [:admin, :reports, @report_type])
end
@report_subtype = report_subtype || report_loader.default_report_subtype
@report_subtypes = report_class.report_subtypes.map do |subtype|
[t("packing.#{subtype}_report", scope: i18n_scope), subtype]
end
end
# Initialize data
params[:display_summary_row] = true if request.get?
@params_fields_to_show = if request.get?
@report.columns.keys - @report.fields_to_hide
else
params[:fields_to_show]
end
def load_form_options
return unless form_options_required?
@data = Reporting::FrontendData.new(spree_current_user)
form_options = Reporting::FrontendData.new(spree_current_user)
@distributors = form_options.distributors.to_a
@suppliers = form_options.suppliers.to_a
@order_cycles = form_options.order_cycles.to_a
end
end
end

View File

@@ -16,16 +16,15 @@ module Admin
respond_to do |format|
format.html do
if view_context.subscriptions_setup_complete?(@shops)
@order_cycles = OrderCycle.joins(:schedules).managed_by(spree_current_user).includes([:distributors, :cached_incoming_exchanges])
@payment_methods = Spree::PaymentMethod.managed_by(spree_current_user).includes(:taggings)
@payment_method_tags = payment_method_tags_by_id
@order_cycles = OrderCycle.joins(:schedules).managed_by(spree_current_user)
@payment_methods = Spree::PaymentMethod.managed_by(spree_current_user)
@shipping_methods = Spree::ShippingMethod.managed_by(spree_current_user)
else
@shop = @shops.first
render :setup_explanation
end
end
format.json { render_as_json @collection, ams_prefix: params[:ams_prefix], payment_method_tags: payment_method_tags_by_id }
format.json { render_as_json @collection, ams_prefix: params[:ams_prefix] }
end
end
@@ -166,21 +165,5 @@ module Admin
@subscription_params ||= PermittedAttributes::Subscription.new(params).call.
to_h.with_indifferent_access
end
def payment_method_tags_by_id
payment_method_tags = ::ActsAsTaggableOn::Tag.
joins(:taggings).
includes(:taggings).
where(taggings: { taggable_type: "Spree::PaymentMethod",
taggable_id: Spree::PaymentMethod.from(Enterprise.managed_by(spree_current_user).
select('enterprises.id').find_by(id: params[:enterprise_id])),
context: 'tags' })
payment_method_tags.each_with_object({}) do |tag, hash|
payment_method_id = tag.taggings.first.taggable_id
hash[payment_method_id] ||= []
hash[payment_method_id] << tag.name
end
end
end
end

View File

@@ -14,7 +14,7 @@ module Api
respond_to :json
def destroy
unless @enterprise.public_send(attachment_name).attached?
unless @enterprise.public_send("#{attachment_name}?")
return respond_with_conflict(error: destroy_attachment_does_not_exist_error_message)
end

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