Compare commits

...

219 Commits
v4.6 ... v4.6.6

Author SHA1 Message Date
Maikel Linke
f6e4b107b0 Update all locales with the latest Transifex translations 2024-09-20 09:46:02 +10:00
Filipe
a5d17b4da9 Merge pull request #12459 from mkllnk/description-html
Sanitise HTML in long description of enterprise
2024-09-19 16:12:45 -06:00
Filipe
83ab9594f6 Merge pull request #12854 from chahmedejaz/task/11200-conditionally-hide-producer-column
[BUU2] Hide producer column when there's only one producer in the admin account
2024-09-19 15:42:15 -06:00
Filipe
562a24524b Merge pull request #12848 from rioug/12770-product-preview
Product preview
2024-09-19 15:20:18 -06:00
Filipe
2809194b42 Merge pull request #12847 from dacook/fix-bug-12835
Fix bug #12835 for producer reports
2024-09-19 14:31:04 -06:00
Maikel
7d3eff2abb Merge pull request #12845 from wandji20/wb-OFN-12281
Fix- chore(deps): bump debounced from 0.0.5 to 1.0.2
2024-09-19 11:22:30 +10:00
David Cook
f8bb33a9e8 Merge pull request #12869 from openfoodfoundation/dependabot/npm_and_yarn/hotwired/turbo-8.0.10
Bump @hotwired/turbo from 8.0.6 to 8.0.10
2024-09-19 09:19:24 +10:00
dependabot[bot]
24a25d31a0 Bump @hotwired/turbo from 8.0.6 to 8.0.10
Bumps [@hotwired/turbo](https://github.com/hotwired/turbo) from 8.0.6 to 8.0.10.
- [Release notes](https://github.com/hotwired/turbo/releases)
- [Commits](https://github.com/hotwired/turbo/compare/8.0.6...v8.0.10)

---
updated-dependencies:
- dependency-name: "@hotwired/turbo"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-18 09:50:44 +00:00
David Cook
4822a9ebcd Merge pull request #12868 from rioug/fix-buu-permission
[BUU] Add missing permission check on product actions
2024-09-18 17:25:24 +10:00
Gaetan Craig-Riou
68fa903d61 Add missing permission check on buu action
Plus request spec
2024-09-18 10:24:09 +10:00
wandji20
c2e0c94f2e Remove unused debounced plugin 2024-09-17 11:56:07 +01:00
David Cook
296997d558 Test to ensure report abilities 2024-09-17 13:23:14 +10:00
David Cook
a9ad6a2851 Grant product managers ability to create reports
We missed this in c31416c, oops.
2024-09-17 13:08:49 +10:00
David Cook
1078e7cd36 Update specs
The key here is the enterprise_relationship. This is required for the supplier to have permission to see the orders.

Curiously, the unit test still passes. All will be revealed in the next commit..
2024-09-17 12:55:22 +10:00
Gaetan Craig-Riou
40c4d38e45 Add permission check 2024-09-17 12:01:53 +10:00
Gaetan Craig-Riou
a25937321a Remove ability of any admin user to see all product
And fix related spec
2024-09-17 11:46:55 +10:00
wandji20
a8db288425 Improve debounced initialised events 2024-09-17 01:56:44 +01:00
dependabot[bot]
a106eb10b6 Bump debounced from 0.0.5 to 1.0.2
Bumps [debounced](https://github.com/hopsoft/debounced) from 0.0.5 to 1.0.2.
- [Release notes](https://github.com/hopsoft/debounced/releases)
- [Commits](https://github.com/hopsoft/debounced/commits/v1.0.2)

---
updated-dependencies:
- dependency-name: debounced
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 01:36:37 +01:00
David Cook
a6d71f8dd1 Merge pull request #12861 from openfoodfoundation/dependabot/npm_and_yarn/express-4.21.0
Bump express from 4.19.2 to 4.21.0
2024-09-17 09:26:41 +10:00
David Cook
5c300d6d41 Merge pull request #12864 from openfoodfoundation/dependabot/npm_and_yarn/floating-ui/dom-1.6.11
Bump @floating-ui/dom from 1.6.10 to 1.6.11
2024-09-17 09:26:24 +10:00
dependabot[bot]
bb4ff5adc2 Bump @floating-ui/dom from 1.6.10 to 1.6.11
Bumps [@floating-ui/dom](https://github.com/floating-ui/floating-ui/tree/HEAD/packages/dom) from 1.6.10 to 1.6.11.
- [Release notes](https://github.com/floating-ui/floating-ui/releases)
- [Changelog](https://github.com/floating-ui/floating-ui/blob/master/packages/dom/CHANGELOG.md)
- [Commits](https://github.com/floating-ui/floating-ui/commits/@floating-ui/dom@1.6.11/packages/dom)

---
updated-dependencies:
- dependency-name: "@floating-ui/dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 09:29:53 +00:00
dependabot[bot]
be548c506d Bump express from 4.19.2 to 4.21.0
Bumps [express](https://github.com/expressjs/express) from 4.19.2 to 4.21.0.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.0)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-15 23:49:21 +00:00
Gaetan Craig-Riou
955f8ba5ae Merge pull request #12858 from openfoodfoundation/dependabot/npm_and_yarn/hotwired/turbo-8.0.6
Bump @hotwired/turbo from 8.0.5 to 8.0.6
2024-09-16 09:48:22 +10:00
Konrad
ad94da975a Add hint about the required Transifex Client 2024-09-14 21:30:54 +02:00
drummer83
f33eb23909 Update all locales with the latest Transifex translations 2024-09-14 20:50:45 +02:00
dependabot[bot]
9ead14b8a0 Bump @hotwired/turbo from 8.0.5 to 8.0.6
Bumps [@hotwired/turbo](https://github.com/hotwired/turbo) from 8.0.5 to 8.0.6.
- [Release notes](https://github.com/hotwired/turbo/releases)
- [Commits](https://github.com/hotwired/turbo/compare/v8.0.5...8.0.6)

---
updated-dependencies:
- dependency-name: "@hotwired/turbo"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-13 09:26:22 +00:00
Gaetan Craig-Riou
38721d9f36 Per review, fix the tab spec
Both tabs have the product name, so add check got the image on the
product details tab.
2024-09-13 14:33:46 +10:00
Gaetan Craig-Riou
3f6aaa74cc Remove duplicated styling for tabs
It uses the same styling as #admin now share via mixins
2024-09-13 14:14:30 +10:00
Ahmed Ejaz
243a4a55b4 11200: add spec for display producer column 2024-09-11 12:03:49 +05:00
Ahmed Ejaz
5be53a40a9 11200: rename products scope 2024-09-11 11:54:38 +05:00
Ahmed Ejaz
76fdf3725a 11200: add explanations 2024-09-11 11:41:01 +05:00
Gaetan Craig-Riou
67f037280a Add comment in shop view file
It wasn't possible to directly reuse the shopfront views because they
are still using angular.
2024-09-11 14:50:37 +10:00
Gaetan Craig-Riou
776b9fcdab Re enable images partial import 2024-09-11 14:24:24 +10:00
Gaetan Craig-Riou
7e84d41e8c Simplify modal opening by just rendering the modal in turbo stream 2024-09-11 14:20:41 +10:00
Gaetan Craig-Riou
68491559f3 Merge pull request #12790 from filipefurtad0/revisit_Orders_and_Distributors_report
Improves test coverage on Orders and Distributors report
2024-09-11 10:28:55 +10:00
Ahmed Ejaz
f8d3467d46 11200: add specs 2024-09-11 01:59:43 +05:00
Ahmed Ejaz
1580d539df 11200: coniditonally hide producer column 2024-09-11 00:56:52 +05:00
Maikel
e2aac8ca1d Merge pull request #12851 from openfoodfoundation/dependabot/npm_and_yarn/jasmine-core-5.3.0
Bump jasmine-core from 5.2.0 to 5.3.0
2024-09-10 13:50:11 +10:00
Maikel
15a2513815 Merge pull request #12849 from openfoodfoundation/dependabot/npm_and_yarn/turbo_power-0.7.0
Bump turbo_power from 0.6.2 to 0.7.0
2024-09-10 13:49:26 +10:00
Gaetan Craig-Riou
00768f6ba0 Add sytem spec for product preview on product edit page 2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
908caa984b Add system spec for product preview 2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
6993750757 Fix product v3 action system spec 2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
379e5acfe5 Fix product preview modal opening
The previous solution failed to take into account that it would have been
trigger on any turbo steam rendering action, not just the product preview
one. Now the open event is dispatched when the product preview
controller is connected, which happens when the modal html is rendered.
2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
5bf6bdf7f0 Fix some display issue with long description 2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
8de7c304fe Add AdminTooltipComponent
I left the stimulus controller separated as it is generic enough
2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
b6695ba9a2 Add product preview on product edit page
Plus translation
2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
e8de76dc46 Add style for Shop
As before, move imported css to partials to avoid duplication. And use
mixin and variable to handle tooltip styling
2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
55733555bf Add style for Product details
Only import relevant css, which has been move to their own partial to
avoid duplication
2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
f59ee96011 Copy foundation-sites css relevant to the modal
The frontend is based on fondation-sites to provide responsive design,
we can't just import in the backend. So opted for copying the part we
needed
2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
2b74bbd45d Add styling for tabs 2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
d56ab9257b Add tab switch and shop tab 2024-09-10 13:29:40 +10:00
Gaetan Craig-Riou
f24a4edc68 Add product detail to the modal 2024-09-10 13:29:39 +10:00
Gaetan Craig-Riou
27dd5def57 Open modal before rendering the received html
This way we don't see a blank modal waiting for the content to load
2024-09-10 13:29:39 +10:00
Gaetan Craig-Riou
561f4648d2 Improve tooltip partial
Set up default value optiona locals variable
2024-09-10 13:29:39 +10:00
Gaetan Craig-Riou
64d3091db9 Add product preview modal
Plus open modal when clicking on "preview" link.
It's using event to communicate between stimulus controller :
https://stimulus.hotwired.dev/reference/controllers#cross-controller-coordination-with-events
2024-09-10 13:29:39 +10:00
Gaetan Craig-Riou
0a9b858f2a Add the ability to pass options ModalComponent
Now you can add another stimulus controller or action to the modal
2024-09-10 13:29:39 +10:00
Gaetan Craig-Riou
4756ab47c2 Wire preview link via turbo-stream 2024-09-10 13:29:39 +10:00
Gaetan Craig-Riou
0a04342712 Remove event listener on disconnect
It prevents memory leak
2024-09-10 13:29:39 +10:00
filipefurtad0
556539d1b1 Removes pending from fixed issue
The pending was not signalling the bug fix as ordering needed to be corrected
2024-09-09 18:07:57 -06:00
filipefurtad0
b7aaab204c Adds timer restriction with Timecop
The datet-time-picker test case was failing for me locally, but passing on GH-Actions. Controlling the time should prevent this type of flakyness
2024-09-09 18:07:57 -06:00
filipefurtad0
632184b0a8 Addresses Davids review 2024-09-09 18:07:57 -06:00
filipefurtad0
8500f6c198 Addresses reviews. The biggest change is moving the table CSS
into its helper, which touches other system specs (namely orders_and_fulfillment_spec.rb).

Rubocop fixup
2024-09-09 18:07:57 -06:00
filipefurtad0
ec4dba71c2 Adds flatpickr test 2024-09-09 18:07:57 -06:00
filipefurtad0
6117d70fae Replaces code with shared examples
This spec was appearently flaky, let's see if this change stabilizes it. It came up here: https://github.com/openfoodfoundation/openfoodnetwork/actions/runs/10639846576/job/29498582671?pr=12790

Removes CSV tests based on permissions

Not sure we need these tests, proposing to remove them and use shared examples to test file download
2024-09-09 18:07:57 -06:00
filipefurtad0
2e5c526170 Adds basic coverage on report file download
Moves file download into report helper

Removes pdf file assertation

Removes test on PDF file on sales tax report

Removes PDF testing from helper
2024-09-09 18:07:57 -06:00
filipefurtad0
32e32117e3 Adds test case around hub filters 2024-09-09 18:07:57 -06:00
filipefurtad0
2a1d494301 Adds coverage for table contents 2024-09-09 18:07:57 -06:00
filipefurtad0
fd45dea9f7 Moves report test case into dedicated file
Sets up an enterprise user instead of an admin user
2024-09-09 18:07:57 -06:00
filipefurtad0
9073f0e5a8 Update all locales with the latest Transifex translations 2024-09-09 10:13:11 -06:00
dependabot[bot]
c4f2c1c3ca Bump jasmine-core from 5.2.0 to 5.3.0
Bumps [jasmine-core](https://github.com/jasmine/jasmine) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/jasmine/jasmine/releases)
- [Changelog](https://github.com/jasmine/jasmine/blob/main/RELEASE.md)
- [Commits](https://github.com/jasmine/jasmine/compare/v5.2.0...v5.3.0)

---
updated-dependencies:
- dependency-name: jasmine-core
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 09:12:18 +00:00
dependabot[bot]
a23bbf8537 Bump turbo_power from 0.6.2 to 0.7.0
Bumps [turbo_power](https://github.com/marcoroth/turbo_power) from 0.6.2 to 0.7.0.
- [Release notes](https://github.com/marcoroth/turbo_power/releases)
- [Commits](https://github.com/marcoroth/turbo_power/compare/v0.6.2...v0.7.0)

---
updated-dependencies:
- dependency-name: turbo_power
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 09:11:49 +00:00
Konrad
6fac32b446 Merge pull request #12799 from chahmedejaz/bugfix/12777-incorrect-invoice-unit-display
Fix Display Unit As field is not Displaying on Invoice and Report
2024-09-06 12:55:03 +02:00
Konrad
cf21c03619 Merge pull request #12827 from johansenja/only-fetch-active-open-shops
Optimise shops page: Only inject distributors with active order cycles
2024-09-05 13:04:17 +02:00
David Cook
0f7f1130f1 Update spec/system/admin/invoice_print_spec.rb 2024-09-05 10:49:45 +02:00
Ahmed Ejaz
009d033e4c 12777: add specs 2024-09-05 10:49:45 +02:00
Ahmed Ejaz
983addff0d 12777: use unit_to_display method for variant unit
- This method prioritize display_as and after that considers options_text
2024-09-05 10:49:45 +02:00
Maikel Linke
d061fe8ad9 Remove unnecessary sanitising
Existing descriptions have been sanitised in a migration. New
descriptions are sanitised when assigned. That should cover everything.
2024-09-05 12:38:33 +10:00
Maikel Linke
53286c22ba Sanitise existing long descriptions of enterprises 2024-09-05 12:07:05 +10:00
Maikel
0cf8f079e4 Merge pull request #12840 from openfoodfoundation/dependabot/github_actions/dot-github/workflows/actions/download-artifact-4.1.7
Bump actions/download-artifact from 3 to 4.1.7 in /.github/workflows
2024-09-05 10:26:20 +10:00
Maikel
f2163a42c4 Merge pull request #12841 from filipefurtad0/reproduce_#12835
Reproduces bug #12835
2024-09-05 10:04:34 +10:00
Maikel Linke
05b25c78bb Bump all artifact actions to v4 2024-09-05 09:57:05 +10:00
filipefurtad0
cc3181c820 Adds unit spec regression test 2024-09-04 16:51:33 -06:00
Joseph Johansen
9cd39d5c91 Improve specificity of closed_shops API test data 2024-09-04 11:46:44 +01:00
Joseph Johansen
7d2f3bfa2f Ensure excluded shops are captured by #closed_shops 2024-09-04 11:46:44 +01:00
Joseph Johansen
6df0b24bcf Add implementation 2024-09-04 11:46:44 +01:00
Joseph Johansen
cf5e182cf7 Add specs for ShopsListService 2024-09-04 11:46:44 +01:00
dependabot[bot]
74bbc7c3c0 Bump actions/download-artifact from 3 to 4.1.7 in /.github/workflows
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.1.7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4.1.7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-04 07:13:07 +00:00
filipefurtad0
4773d1c82e Reproduces bug #12835 2024-09-03 20:15:29 -06:00
Maikel
fde18ebf24 Merge pull request #12839 from johansenja/include-hidden-files-code-cov-chunk-upload
Enable include-hidden-files for uploading code coverage chunks
2024-09-04 10:19:47 +10:00
Joseph Johansen
fd2cbb67db Enable include-hidden-files for upload code coverage chunks 2024-09-03 18:50:34 +01:00
Maikel
3f1d99d77c Merge pull request #12831 from mkllnk/anonymous-orders
Share anonymised sales data on DFC API with authorised users
2024-09-03 10:59:30 +10:00
David Cook
9cfcab4f02 Merge pull request #12834 from rioug/12832-Fix-karma-test
Fix karma test
2024-09-02 14:08:23 +10:00
Gaetan Craig-Riou
703ad26773 Fix spec to make the test usefull 2024-09-02 12:23:27 +10:00
Gaetan Craig-Riou
627c9eede2 Fix spec using confirm pop up
For some reason, karma hang and fails with a timeout error if javascrpit
`confirm` pop up isn't mocked.

Plus fix spec to actually check the pop up has been displayed
2024-09-02 12:18:44 +10:00
David Cook
f9a76342f8 Merge pull request #12830 from filipefurtad0/remove_pdf_file_test_from_reports
Removes test on PDF file on sales tax report
2024-09-02 11:15:13 +10:00
Maikel Linke
d52134dad8 Filter sales data by dates 2024-08-30 15:00:06 +10:00
Maikel Linke
1016656781 Publish data only of participating distributors 2024-08-30 14:34:39 +10:00
Maikel Linke
bd1611630f Build DFC data for sales 2024-08-30 14:34:32 +10:00
Maikel Linke
ce28c10c7e Move sales data generation to a service object
There will be lots and lots. The sales data root object is also the
authenticated person. The data has its own URL (semantic id) which
doens't need to contain the user id.

The service object can also be tested more easily. I'm setting up the
test data here.
2024-08-30 14:30:46 +10:00
Maikel Linke
4342d3b912 Add DFC API endpoint for sales data 2024-08-30 14:30:46 +10:00
Maikel Linke
af3aed827a Update all locales with the latest Transifex translations 2024-08-30 09:59:49 +10:00
Maikel
f73be6447e Merge pull request #12824 from chahmedejaz/task/12823-fully-enable-admin-style-v3
[BUU] Fully enable admin_style_v3
2024-08-30 09:56:23 +10:00
Filipe
98eabc9d0f Merge pull request #12826 from chahmedejaz/bugfix/12815-fix-inconsistent-unit-values
Fix Inconsistent Behavior When Editing Products to mg Units in Hungarian Locale
2024-08-29 17:47:44 -06:00
Filipe
169cbbe1a1 Merge pull request #12793 from rioug/12768_fix_bulk_coop_report
Fix bulk coop report
2024-08-29 15:20:48 -06:00
filipefurtad0
72a503c3c1 Removes test on PDF file on sales tax report 2024-08-29 11:46:22 -06:00
Konrad
afc4c1e967 Merge pull request #12760 from wandji20/wb-OFN-12744
UX improvements for creation of new products [OFN-12744]
2024-08-29 17:22:36 +02:00
Rachel Arnould
d76c4bddb0 Merge pull request #12806 from drummer83/typo
Fix typos in "category" and unify capitals on "Back To Xyz List" buttons
2024-08-29 16:40:35 +02:00
Konrad
ae993784d8 Merge pull request #12813 from chahmedejaz/task/12810-increase-price-column-width
[BUU] Fix the too Narrow Price field
2024-08-29 15:22:27 +02:00
Filipe
d1abe22c32 Merge pull request #12805 from mkllnk/update-wicked-pdf-config-syntax
Update deprecated WickedPdf config syntax
2024-08-28 21:35:40 -06:00
Filipe
2817b8891e Merge pull request #12814 from dacook/buu/cell-padding
Increase column space in bulk products table
2024-08-28 21:30:03 -06:00
Filipe
ab87610d91 Merge pull request #12807 from kernal053/fix-broken-column-after-cloning
Fix broken column after cloning product
2024-08-28 21:03:53 -06:00
David Cook
54252f5444 Add comment 2024-08-29 09:42:59 +10:00
Ahmed Ejaz
7b6b0dbb78 12815: use en formatting for unit value conversion 2024-08-29 03:36:46 +05:00
Maikel
b2e15f52cf Merge pull request #12822 from johansenja/include-coverage-assets
Fix artifact path for simplecov report upload
2024-08-29 08:10:38 +10:00
Ahmed Ejaz
5d18c48b6c 12823: fully enable admin_style_v3 flag 2024-08-29 02:09:29 +05:00
Konrad
0c2dcbc50d Merge pull request #12812 from chahmedejaz/bugfix/12809-irresponsive-products-column-widths
[BUU] Fix Table width not responsive to the amount of selected columns
2024-08-28 16:45:07 +02:00
Joseph Johansen
4a028b2238 Fix artifact path for simplecov report upload 2024-08-28 11:09:01 +01:00
Maikel
43a366005c Merge pull request #12798 from johansenja/enable-simplecov
Set up code coverage metrics with simplecov
2024-08-28 12:07:53 +10:00
Maikel Linke
64470e977a Avoid name clash in simplecov task
A variable and a method were called the same.
Also made method calls more obvious with parenthesis.
2024-08-28 11:31:01 +10:00
Maikel Linke
a696c66857 Clean up tmp dir after test and avoid collisions
Best viewed ignoring whitespace changes.
2024-08-28 11:27:42 +10:00
Maikel Linke
cfeb0afbd4 Update all locales with the latest Transifex translations 2024-08-28 09:45:24 +10:00
Maikel Linke
4968e3dc8d Update all locales with the latest Transifex translations 2024-08-28 09:12:52 +10:00
David Cook
5324747f89 Reduce cell padding
This is closer to the original design:
* 6px between inputs
* 6px vertical padding on condensed rows
* 12px vertical padding on relaxed rows

Note that 'relaxed' rows are now smaller than the regular rows, which was not the original intention. But we haven't got spare time to do a broader review of table styles right now.
2024-08-26 16:53:19 +10:00
Gaetan Craig-Riou
ef2856d169 Remove added eventListener on disconnect
It's good practise to remove added event listener to avoid memory leak
2024-08-26 11:15:26 +10:00
Ahmed Ejaz
d9c79ee49c 12810: increase price width
- make it to 10% which makes sure that any price value acceptable by the system is displayed fully
- Reduce On Hand to 8% to make up for some space for the above
2024-08-24 17:37:59 +05:00
Ahmed Ejaz
d1f9b0855d 12809: fix the class name for the producer column in product 2024-08-24 02:05:04 +05:00
drummer83
b82726e7ba Unify the capital letters of the "Back To Xyz List" buttons and update specs
I decided to use the most frequently used version as the default, which is every word beginning with a capital letter
2024-08-23 13:23:57 +02:00
drummer83
7e7ab2e36d Fix typo in "categeory" (additional e) and update specs 2024-08-23 13:20:46 +02:00
kernal053
e35a5179bb Fix broken column after cloning product 2024-08-23 16:01:56 +05:30
Joseph Johansen
85385a1989 Rename uploads so combined report is listed first alphabetically 2024-08-23 11:26:45 +01:00
Joseph Johansen
a816814819 Update CI workflow to upload results and call rake task 2024-08-23 11:26:41 +01:00
wandji20
94b98867d8 Revert use of searchableDropdownComponent for product unit 2024-08-23 09:57:37 +01:00
wandji20
35ef1b9c7f Refactor new product dropdown to use SeachableDropdown component [OFN-12744] 2024-08-23 09:57:37 +01:00
wandji20
8badfb2505 Allow extra attributes to be passed to searchable dropdown component [OFN-12744] 2024-08-23 09:57:37 +01:00
wandji20
d61acd2cc1 Unify error messages and display on new product form [OFN-12744] 2024-08-23 09:57:37 +01:00
wandji20
7417cee20a Fix leaked trix editor event listener [OFN-12744] 2024-08-23 09:57:37 +01:00
Maikel Linke
d489c77efe Update deprecated WickedPdf config syntax
Avoids warning:

> WickedPdf.config= is deprecated and will be removed in future versions. Use WickedPdf.configure instead.
2024-08-23 15:57:59 +10:00
Maikel
e2423ad612 Merge pull request #12800 from filipefurtad0/adds_retry_option_on_edit_spec
Adds retry option to flaky edit_spec.rb
2024-08-23 12:02:59 +10:00
filipefurtad0
8b036113d9 Update all locales with the latest Transifex translations 2024-08-22 17:33:21 -06:00
Maikel
7f09044ae1 Merge pull request #12755 from johansenja/optimise-shops-page6
Optimise shops page: Enable injected enterprise data to be scoped to specific enterprise ids
2024-08-23 09:26:22 +10:00
Maikel
e9c7e1778c Merge pull request #12782 from mkllnk/reports
Add fallback report loading in case websockets fail
2024-08-23 09:23:42 +10:00
filipefurtad0
32cd14ef54 Adds slep(2) 2024-08-22 15:55:31 -06:00
filipefurtad0
ad585f1eab Adds retry option to flaky test case
...does so on another flaky test case
2024-08-22 15:55:31 -06:00
Filipe
d9368c1bfc Merge pull request #12781 from wandji20/wb-OFN-12775
Add warning popup to order cycle list [OFN-12775]
2024-08-22 15:39:27 -06:00
wandji20
b6bfb4e866 Refactor order cycle same_dates method 2024-08-22 22:21:17 +01:00
wandji20
4d222c61c6 Improve readability of order_cycle_set process method 2024-08-22 22:17:12 +01:00
wandji20
d599cf77a2 Fix failing specs 2024-08-22 22:17:12 +01:00
wandji20
8f7505d53d Refactor oc datetime content partial and include warning modal in oc list [OFN-12775] 2024-08-22 22:17:12 +01:00
wandji20
867e17301f Support passing oc confirmation params for oc list [OFN-12775] 2024-08-22 22:17:12 +01:00
wandji20
95135ca526 Move order_cycle datetime verification logic to service files [OFN-12775] 2024-08-22 22:17:12 +01:00
wandji20
de063fecb1 Move same_datetime_value method to OrderCycle model [OFN-12775] 2024-08-22 22:17:12 +01:00
Filipe
ef9ca33913 Merge pull request #12772 from drummer83/E500_sub
Display admin order page instead of shopfront order page to avoid error 500
2024-08-22 13:26:20 -06:00
Konrad
2710eafc33 Merge pull request #12779 from EdwardLi-coder/clearer_error_message_and_clean_up
Clearer error message and clean up for Product Categories
2024-08-22 18:15:50 +02:00
Joseph Johansen
4718fdb0be Optimise Spree::Taxon.supplied_taxons 2024-08-22 17:41:00 +02:00
Joseph Johansen
ce6ae04147 Add spec for confirming correct values when scoped 2024-08-22 17:41:00 +02:00
johansenja
1621f97fdb Use subject method in spec
Co-authored-by: Gaetan Craig-Riou <40413322+rioug@users.noreply.github.com>
2024-08-22 17:41:00 +02:00
Joseph Johansen
96f9894f41 Add enterprise_ids to cache key 2024-08-22 17:41:00 +02:00
Joseph Johansen
66b519bd1c Undo minor unneeded changes 2024-08-22 17:41:00 +02:00
Joseph Johansen
1b8e256e8a Add unit tests 2024-08-22 17:41:00 +02:00
Joseph Johansen
b73e529bfc Scope injected enterprise properties to specific enterprises 2024-08-22 17:41:00 +02:00
Konrad
25b1620707 Merge pull request #12743 from wandji20/wb-OFN-12214
(Fix) chore(deps): bump wicked_pdf from 2.6.3 to 2.8.0 [OFN-12214]
2024-08-22 17:34:20 +02:00
Joseph Johansen
5f9b14df9f Implement rake task to combine results 2024-08-21 13:02:31 +01:00
Joseph Johansen
922b853e3a Define specs for rake task to combine results 2024-08-21 13:02:31 +01:00
Joseph Johansen
8d747a2508 Enable coverage in base_spec_helper 2024-08-21 13:02:31 +01:00
Gaetan Craig-Riou
a50be52cde Merge pull request #12792 from openfoodfoundation/dependabot/npm_and_yarn/elliptic-6.5.7
Bump elliptic from 6.5.4 to 6.5.7
2024-08-21 09:56:19 +10:00
Gaetan Craig-Riou
d62d002bc5 Merge pull request #12780 from dacook/optimise-12714
Optimise subscriptions admin
2024-08-21 09:50:27 +10:00
Maikel
0c7448ba43 Merge pull request #12726 from mkllnk/order-stock-spec
Track (negative) stock for on-demand products and overrides
2024-08-20 15:05:16 +10:00
Gaetan Craig-Riou
24afd40414 Fix bulk coop supplier report 2024-08-20 14:32:35 +10:00
Maikel
524aec7868 Merge pull request #12788 from openfoodfoundation/dependabot/npm_and_yarn/mrujs-1.0.2
Bump mrujs from 1.0.1 to 1.0.2
2024-08-20 14:30:47 +10:00
David Cook
f2eb4b05f4 Avoid copying gigantic array 2024-08-20 14:00:13 +10:00
David Cook
ffaf1b4ea0 Cache distributor 2024-08-20 14:00:13 +10:00
David Cook
eb547f4861 Add test on number of db queries
Hmm, I think I seen an opportunity to clean up already.
2024-08-20 14:00:13 +10:00
David Cook
c9daca22d5 Rename spec to match class name 2024-08-20 14:00:13 +10:00
dependabot[bot]
7b22740289 Bump elliptic from 6.5.4 to 6.5.7
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.4 to 6.5.7.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.4...v6.5.7)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-20 03:52:13 +00:00
Maikel
66c8a5c424 Merge pull request #12789 from chahmedejaz/bugfix/12783-fix-artifact-upload-names-conflict
Fix Artifact Upload Conflicts with Unique Node-Based Names
2024-08-20 13:51:13 +10:00
David Cook
cfeac651b6 Merge pull request #12785 from filipefurtad0/spec_for_#12768
Reproduces S2 bug #12768
2024-08-20 13:45:07 +10:00
EdwardLi-coder
05315ff8e0 delete spree.new_taxon 2024-08-20 08:11:49 +08:00
EdwardLi-coder
c4ee6b14ff clear error message and clean up 2024-08-20 08:11:49 +08:00
filipefurtad0
a78f46259c Asserts on the flash warning first
The warning first displays "Saving..." before confirming changes are saved.
I'm not entirelly sure, but it seems that asserting on this first, before asserting on other page elements stabilizes the spec.
2024-08-19 13:58:03 -06:00
filipefurtad0
1e79fde236 Reproduces S2 bug #12768 2024-08-19 13:58:03 -06:00
Ahmed Ejaz
a9fe52a4ff Revert "12783 - validate artifact upload"
This reverts commit 075f5499f8.
2024-08-19 23:29:12 +05:00
Ahmed Ejaz
075f5499f8 12783 - validate artifact upload 2024-08-19 23:16:39 +05:00
Ahmed Ejaz
ed61f7e7bc 12783: use unique artifact name based on node index 2024-08-19 22:57:04 +05:00
dependabot[bot]
bd6019036e Bump mrujs from 1.0.1 to 1.0.2
Bumps [mrujs](https://github.com/KonnorRogers/mrujs) from 1.0.1 to 1.0.2.
- [Changelog](https://github.com/KonnorRogers/mrujs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/KonnorRogers/mrujs/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: mrujs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 09:31:52 +00:00
Konrad
0bbc3d2758 Merge pull request #12766 from mkllnk/magick-dep
Remove direct dependency on MiniMagick
2024-08-18 18:39:48 +02:00
Konrad
ae6182579b Merge pull request #12762 from EdwardLi-coder/change_colour_of_complete_order
change colour of "complete order"
2024-08-18 15:08:46 +02:00
Konrad
1e05811917 Merge pull request #12745 from johansenja/optimise-shops-page5
Improve effiency of OrderCycle.earliest_closing_times
2024-08-18 14:40:49 +02:00
Konrad
5f86a26f42 Merge pull request #12733 from wandji20/wb-OFN-6567
Update product variant unit display name, price, and total price width on different screen sizes [OFN-6567]
2024-08-18 13:25:24 +02:00
Konrad
3f1b907ef2 Merge pull request #12740 from wandji20/wb-OFN-12532-v1
Pluralize admin products search result [OFN-12532-v1]
2024-08-18 12:18:18 +02:00
Maikel Linke
d9c296cdb3 Stabilise flaky report specs
It looks like we have a new race condition that may only be a problem in
specs. If you trigger one report, it displays via websockets and then
you trigger the next report, there may still be some Javascript active
that displays the first report while the second one is loading. I'm not
sure if users would navigate that fast though.

To minimise the problem, I adjusted the polling to leave more room for
the default websockets response.
2024-08-16 17:08:57 +10:00
Maikel Linke
23aa762be2 Add fallback report loading in case websockets fail
This also resolves a race condition scenario. Even if the report gets
rendered via websockets before the controller response is rendered then
the fallback script loads the report again. It's not the most beautiful
but probably okay until we replace websockts altogether.

I'm leaving websockets in at the moment because it can render the report
much quicker than polling can.
2024-08-16 15:24:34 +10:00
Maikel Linke
61f2954973 Add TurboPower Rails gem for nice helpers
The helpers are more convenient but also allow us to add options like
smooth scrolling. I thought that looked nicer and is less confusing.

Please note that the `scroll_into_view` helper uses the `targets`
attribute instead of `target`. That attribute needs CSS selectors with a
leading `#` for ids.
2024-08-16 14:37:57 +10:00
Maikel Linke
d354317c73 Replace cable_ready report loading w/ Turbo stream
I'm adding TurboPower for the scroll_into_view action. It adds all the
nice CableReady actions to Turbo Streams.

Note that I omitted `block: "start"` because that option is the default
in Javascript. And the generic `action` method doesn't support
parameters like this anyway. I'll work on that in the next commit.

I also re-introduced a race condition by rendering the "loading"
indicator after triggering the report rendering job. I'm planning to
resolve that later.
2024-08-16 14:37:57 +10:00
Maikel Linke
19ef047193 Create observable reports blob early
This will allow us to check for completion of the report later in case
websockets fail.
2024-08-16 14:37:57 +10:00
Maikel Linke
037eb456c0 Remove unused controller ivar 2024-08-16 14:37:57 +10:00
Maikel Linke
aed78f3138 Simplify reports controller code branching 2024-08-16 14:37:57 +10:00
Maikel Linke
c31416c536 Separate showing and rendering report 2024-08-16 14:37:57 +10:00
drummer83
f154de66f9 Display admin order page instead of shopfront order page to avoid error 500 2024-08-14 14:44:59 +02:00
wandji20
4a30493716 Improve code style [OFN-12214] 2024-08-13 18:29:27 +01:00
wandji20
f325857e1f Strip end of long invoice table header names due to names not properly parsed when converting pdf to text [OFN-12214] 2024-08-13 18:23:27 +01:00
wandji20
58872a7017 Include mail stylsheet tag in invoice pdf files and mailer layout [OFN-12214] 2024-08-13 18:23:18 +01:00
wandji20
7392079d4d Move mail related styles to asset pipeline [OFN-12214] 2024-08-13 17:11:29 +01:00
wandji20
506126c1d3 Bump wicked_pdf to version 2.8.1 2024-08-13 17:11:29 +01:00
wandji20
fa004d0897 Remove wicked pdf stylsheet include tag in order report pdf [OFN-12214] 2024-08-13 17:11:29 +01:00
dependabot[bot]
db7add88fe chore(deps): bump wicked_pdf from 2.6.3 to 2.8.0
Bumps [wicked_pdf](https://github.com/mileszs/wicked_pdf) from 2.6.3 to 2.8.0.
- [Release notes](https://github.com/mileszs/wicked_pdf/releases)
- [Changelog](https://github.com/mileszs/wicked_pdf/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mileszs/wicked_pdf/commits)

---
updated-dependencies:
- dependency-name: wicked_pdf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-13 17:11:29 +01:00
Maikel Linke
f21aca234c Remove direct dependency on MiniMagick
We still depend on it as long as we set it as image processor but now we
can switch to another image processor without changing the code around
error handling.

We now rescue from unknown errors during image processing which should
make the app more robust.
2024-08-13 15:25:58 +10:00
EdwardLi-coder
93a6ff4b50 remove background-color 2024-08-13 09:38:20 +08:00
EdwardLi-coder
97a72dfde7 change colour of "complete order" 2024-08-12 12:01:02 +08:00
Joseph Johansen
5ca7f40a4e Add unit test 2024-08-06 16:12:13 +01:00
Joseph Johansen
a2f4df191a Improve effiency of OrderCycle.earliest_closing_times 2024-08-06 16:12:13 +01:00
wandji20
b49da46842 Pluralize admin products search result [OFN-12532-v1] 2024-08-06 08:52:51 +01:00
wandji20
2d24593403 Update product variant unit display name, price, and total price widths on different screen sizes [OFN-6567] 2024-08-02 13:03:42 +01:00
Maikel Linke
2201d2e8c2 VariantOverride with on_demand now overriding stock
Otherwise we would try to take stock from the producer stock level
without respecting their on-demand settings. So from now on:
If stock level or on_demand are set on the override then it's not using
producer stock levels.
2024-08-02 14:40:17 +10:00
Maikel Linke
b6c407971d Allow on-demand VariantOverride to track stock
We allowed this for producer stock and need to do the same for inventory
stock. This will allow us to create backorders for missing, but promised
stock.
2024-08-02 14:40:17 +10:00
Maikel Linke
cd8dc41b15 Update stock specs and add pending cases 2024-08-02 14:40:17 +10:00
Maikel Linke
a1887bdc76 Update stock levels of on-demand items
We weren't bothering with stock when items were on demand anyway. But we
want to track stock now so that we can backorder more when local stock
levels become negative.
2024-08-02 14:40:17 +10:00
Maikel Linke
e9f89362f4 Remove validation of positive stock when on demand
We weren't allowing negative stock to stop any bug from accidentally
drawing too much stock. But now we want to implement a backordering
logic that depends on negative stock levels to know how much is needed
to replenish stock levels.
2024-08-02 14:40:17 +10:00
Maikel Linke
675b7febdf Test stock logic on variant level
VariantOverrides are bolted onto variants to change their logic.
2024-08-02 14:40:17 +10:00
Maikel Linke
90fdf59415 Test current stock logic on shipment level
During checkout, stock is adjusted when a shipment is finalised. The
chain is:

* Order state change to complete.
* Trigger Order#finalize! which updates shipments.
* Trigger Shipment#finalize! which adjusts stock on the variant.
* A variant holds stock in stock items or in a variant override.
2024-08-02 14:40:17 +10:00
231 changed files with 4436 additions and 1898 deletions

View File

@@ -11,6 +11,7 @@ assignees: ''
- [ ] Merge pull requests in the [Ready To Go] column
- [ ] Include translations: `script/release/update_locales`
- You need the [Transifex Client] installed on your local dev environement to run the script.
- [ ] Increment version number: `git push upstream HEAD:refs/tags/vX.Y.Z`
- Major: if server changes are required (eg. provision with ofn-install)
- Minor: larger change that is irreversible (eg. migration deleting data)
@@ -53,3 +54,4 @@ The full process is described at https://github.com/openfoodfoundation/openfoodn
[#global-community]: https://app.slack.com/client/T02G54U79/C59ADD8F2
[Create issue]: https://github.com/openfoodfoundation/openfoodnetwork/issues/new?assignees=&labels=&projects=&template=release.md&title=Release
[#core-devs]: https://openfoodnetwork.slack.com/archives/GK2T38QPJ
[Transifex Client]: https://developers.transifex.com/docs/cli

View File

@@ -3,7 +3,7 @@ name: Build
on:
workflow_dispatch:
push:
branches-ignore:
branches-ignore:
- 'dependabot/**'
pull_request:
@@ -47,7 +47,7 @@ jobs:
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
with:
redis-version: 6
- name: Set up Ruby
@@ -81,11 +81,20 @@ jobs:
# RSpec split test files by test examples feature - it's optional
# https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it
#KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES: true
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/controllers/**/*_spec.rb}"
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/controllers/**/*_spec.rb}"
run: |
git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
bin/rake knapsack_pro:rspec
- name: Save SimpleCov file
uses: actions/upload-artifact@v4
with:
name: simplecov-chunk-controllers-${{ matrix.ci_node_index }}
path: coverage/*.*
retention-days: 2 # doesn't need to be long, because it's the combined results that matter
if-no-files-found: ignore
include-hidden-files: true
models:
runs-on: ubuntu-22.04
services:
@@ -116,7 +125,7 @@ jobs:
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
with:
redis-version: 6
- name: Set up Ruby
@@ -141,10 +150,19 @@ jobs:
# RSpec split test files by test examples feature - it's optional
# https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it
#KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES: true
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/models/**/*_spec.rb}"
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/models/**/*_spec.rb}"
run: |
bin/rake knapsack_pro:rspec
- name: Save SimpleCov file
uses: actions/upload-artifact@v4
with:
name: simplecov-chunk-models-${{ matrix.ci_node_index }}
path: coverage/*.*
retention-days: 2 # doesn't need to be long, because it's the combined results that matter
if-no-files-found: ignore
include-hidden-files: true
system_admin:
runs-on: ubuntu-22.04
services:
@@ -175,7 +193,7 @@ jobs:
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
with:
redis-version: 6
- name: Set up Ruby
@@ -209,16 +227,25 @@ jobs:
# RSpec split test files by test examples feature - it's optional
# https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it
#KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES: true
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/system/admin/**/*_spec.rb}"
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/system/admin/**/*_spec.rb}"
run: |
bin/rake knapsack_pro:queue:rspec
- name: Save SimpleCov file
uses: actions/upload-artifact@v4
with:
name: simplecov-chunk-system-admin-${{ matrix.ci_node_index }}
path: coverage/*.*
retention-days: 2 # doesn't need to be long, because it's the combined results that matter
if-no-files-found: ignore
include-hidden-files: true
- name: Archive failed tests screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: failed-admin-tests-screenshots
name: failed-admin_${{ matrix.ci_node_index }}-tests-screenshots
path: tmp/capybara/screenshots/*.png
retention-days: 7
if-no-files-found: ignore
@@ -247,13 +274,13 @@ jobs:
ci_node_total: [12]
# Indexes for parallel jobs (starting from zero).
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
steps:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
with:
redis-version: 6
- name: Set up Ruby
@@ -287,16 +314,25 @@ jobs:
# RSpec split test files by test examples feature - it's optional
# https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it
#KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES: true
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/system/consumer/**/*_spec.rb}"
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/system/consumer/**/*_spec.rb}"
run: |
bin/rake knapsack_pro:queue:rspec
- name: Save SimpleCov file
uses: actions/upload-artifact@v4
with:
name: simplecov-chunk-system-consumer-${{ matrix.ci_node_index }}
path: coverage/*.*
retention-days: 2 # doesn't need to be long, because it's the combined results that matter
if-no-files-found: ignore
include-hidden-files: true
- name: Archive failed tests screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: failed-consumer-tests-screenshots
name: failed-consumer_${{ matrix.ci_node_index }}-tests-screenshots
path: tmp/capybara/screenshots/*.png
retention-days: 7
if-no-files-found: ignore
@@ -331,7 +367,7 @@ jobs:
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
with:
redis-version: 6
- name: Set up Ruby
@@ -371,6 +407,15 @@ jobs:
run: |
bin/rake knapsack_pro:rspec
- name: Save SimpleCov file
uses: actions/upload-artifact@v4
with:
name: simplecov-chunk-engines-${{ matrix.ci_node_index }}
path: coverage/*.*
retention-days: 2 # doesn't need to be long, because it's the combined results that matter
if-no-files-found: ignore
include-hidden-files: true
test_the_rest:
runs-on: ubuntu-22.04
services:
@@ -401,7 +446,7 @@ jobs:
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
with:
with:
redis-version: 6
- name: Set up Ruby
@@ -439,6 +484,15 @@ jobs:
run: |
bin/rake knapsack_pro:rspec
- name: Save SimpleCov file
uses: actions/upload-artifact@v4
with:
name: simplecov-chunk-the-rest-${{ matrix.ci_node_index }}
path: coverage/*.*
retention-days: 2 # doesn't need to be long, because it's the combined results that matter
if-no-files-found: ignore
include-hidden-files: true
non_knapsack_jest_karma:
runs-on: ubuntu-22.04
services:
@@ -476,3 +530,39 @@ jobs:
- name: Run jest tests
run: yarn jest
collate_simplecov_results:
runs-on: ubuntu-22.04
needs:
- controllers
- models
- engines
- system_admin
- system_consumer
- test_the_rest
steps:
- uses: actions/checkout@v3
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Download individual results from individual runners
uses: actions/download-artifact@v4
with:
pattern: simplecov-chunk-*
path: tmp/simplecov
merge-multiple: true
- name: collate results from each of the workers
run: bundle exec rake 'simplecov:collate_results[tmp/simplecov]'
- name: Upload collated results
uses: actions/upload-artifact@v4
with:
name: combined-simplecov-report
path: coverage/**/*.*
retention-days: 7
if-no-files-found: ignore
include-hidden-files: true

View File

@@ -14,4 +14,6 @@ SimpleCov.start 'rails' do
add_filter '/log'
add_filter '/db'
add_filter '/lib/tasks/sample_data/'
formatter SimpleCov::Formatter::SimpleFormatter
end

View File

@@ -104,6 +104,7 @@ gem 'sidekiq-scheduler'
gem "cable_ready"
gem "stimulus_reflex"
gem "turbo_power"
gem "turbo-rails"
gem 'combine_pdf'

View File

@@ -785,6 +785,8 @@ GEM
actionpack (>= 6.0.0)
activejob (>= 6.0.0)
railties (>= 6.0.0)
turbo_power (0.6.2)
turbo-rails (>= 1.3.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
@@ -835,7 +837,7 @@ GEM
websocket-extensions (0.1.5)
whenever (1.0.0)
chronic (>= 0.6.3)
wicked_pdf (2.6.3)
wicked_pdf (2.8.1)
activesupport
wkhtmltopdf-binary (0.12.6.7)
xml-simple (1.1.8)
@@ -973,6 +975,7 @@ DEPENDENCIES
stripe
timecop
turbo-rails
turbo_power
valid_email2
validates_lengths_from_database
vcr

View File

@@ -1,7 +1,11 @@
angular.module("admin.orderCycles").controller "OrderCyclesCtrl", ($scope, $q, Columns, StatusMessage, RequestMonitor, OrderCycles, Enterprises, Schedules, Dereferencer) ->
$scope.RequestMonitor = RequestMonitor
$scope.columns = Columns.columns
$scope.saveAll = -> OrderCycles.saveChanges($scope.order_cycles_form)
$scope.saveAll = ($event) ->
trigger_action = $($event.target).data('trigger-action')
confirm = $($event.target).data('confirm')
OrderCycles.saveChanges($scope.order_cycles_form, { trigger_action, confirm })
$scope.ordersCloseAtLimit = -31 # days
$scope.resetSelectFilters = ->

View File

@@ -29,13 +29,13 @@ angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCy
deferred.reject(response)
deferred.promise
saveChanges: (form) ->
saveChanges: (form, params = {}) ->
changed = {}
for id, orderCycle of @byID when not @saved(orderCycle)
changed[Object.keys(changed).length] = @changesFor(orderCycle)
if Object.keys(changed).length > 0
StatusMessage.display('progress', "Saving...")
OrderCycleResource.bulkUpdate { order_cycle_set: { collection_attributes: changed } }, (data) =>
OrderCycleResource.bulkUpdate { order_cycle_set: { collection_attributes: changed }, confirm: params['confirm'], trigger_action: params['trigger_action'] }, (data) =>
for orderCycle in data
delete orderCycle.coordinator
delete orderCycle.producers
@@ -47,8 +47,10 @@ angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCy
, (response) =>
if response.data.errors?
StatusMessage.display('failure', response.data.errors[0])
else if (response.data.trigger_action)
StatusMessage.display('notice', t('js.order_cycles.unsaved_changes'), response.data.trigger_action)
else
StatusMessage.display('failure', "Oh no! I was unable to save your changes.")
StatusMessage.display('failure', t('js.order_cycles.bulk_save_error'))
saved: (order_cycle) ->
@diff(order_cycle).length == 0

View File

@@ -1,2 +1,3 @@
- # NOTE: make sure that any changes in this template are reflected in app/views/admin/products_v3/product_preview.turbo_stream.haml
%li{ "ng-class": "{active: selector.active}" }
%a{ tooltip: "{{selector.object.value}}", "tooltip-placement": "bottom", "ng-transclude": true, "ng-class": "{active: selector.active, 'has-tip': selector.object.value}" }

View File

@@ -1,3 +1,4 @@
- # NOTE: make sure that any changes in this template are reflected in app/views/admin/products_v3/product_preview.turbo_stream.haml
%ul
%active-selector{ "ng-repeat": "selector in allSelectors", "ng-show": "ifDefined(selector.fits, true)" }
%span{"ng-bind" => "::selector.object.name"}

View File

@@ -1,3 +1,4 @@
- # NOTE: make sure that any changes in this template are reflected in app/views/admin/products_v3/product_preview.turbo_stream.haml
.row
.columns.small-12.medium-6.large-6.product-header
%h3{"ng-bind" => "::product.name"}

View File

@@ -0,0 +1 @@
@import './mail/all.scss';

View File

@@ -0,0 +1,3 @@
@import '../../../webpacker/css/admin/globals/palette.scss';
@import 'email';
@import 'payments_list';

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
class AdminTooltipComponent < ViewComponent::Base
def initialize(text:, link_text:, placement: "top", link: "", link_class: "")
@text = text
@link_text = link_text
@placement = placement
@link = link
@link_class = link_class
end
end

View File

@@ -0,0 +1,8 @@
%div{"data-controller": "tooltip", "data-tooltip-placement-value": @placement }
%a{"data-tooltip-target": "element", href: @link, class: @link_class}
= @link_text
.tooltip-container
.tooltip{"data-tooltip-target": "tooltip"}
= sanitize @text
.arrow{"data-tooltip-target": "arrow"}

View File

@@ -1,11 +1,15 @@
# frozen_string_literal: true
class ModalComponent < ViewComponent::Base
def initialize(id:, close_button: true, instant: false, modal_class: :small)
def initialize(id:, close_button: true, instant: false, modal_class: :small, **options)
@id = id
@close_button = close_button
@instant = instant
@modal_class = modal_class
@options = options
@data_controller = "modal #{@options.delete(:'data-controller')}".squish
@data_action =
"keyup@document->modal#closeIfEscapeKey #{@options.delete(:'data-action')}".squish
end
private

View File

@@ -1,4 +1,4 @@
%div{ id: @id, "data-controller": "modal", "data-action": "keyup@document->modal#closeIfEscapeKey", "data-modal-instant-value": @instant }
%div{ id: @id, "data-controller": @data_controller, "data-action": @data_action, "data-modal-instant-value": @instant, **@options }
.reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" }
.reveal-modal.fade.modal-component{ "data-modal-target": "modal", class: @modal_class }
= content

View File

@@ -11,7 +11,8 @@ class SearchableDropdownComponent < ViewComponent::Base
selected_option:,
placeholder_value:,
include_blank: false,
aria_label: ''
aria_label: '',
other_attrs: {}
)
@f = form
@name = name
@@ -20,11 +21,13 @@ class SearchableDropdownComponent < ViewComponent::Base
@placeholder_value = placeholder_value
@include_blank = include_blank
@aria_label = aria_label
@other_attrs = other_attrs
end
private
attr_reader :f, :name, :options, :selected_option, :placeholder_value, :include_blank, :aria_label
attr_reader :f, :name, :options, :selected_option, :placeholder_value, :include_blank,
:aria_label, :other_attrs
def classes
"fullwidth #{remove_search_plugin? ? 'no-input' : ''}"

View File

@@ -1 +1 @@
= f.select name, options_for_select(options, selected_option), { include_blank: }, class: classes, data:, 'aria-label': aria_label
= f.select name, options_for_select(options, selected_option), { include_blank: }, class: classes, data:, 'aria-label': aria_label, **other_attrs

View File

@@ -8,6 +8,10 @@ export default class extends Controller {
window.addEventListener("click", this.#hideIfClickedOutside);
}
disconnect() {
window.removeEventListener("click", this.#hideIfClickedOutside);
}
toggle() {
this.contentTarget.classList.toggle("show");
}

View File

@@ -2,6 +2,8 @@
module Admin
class OrderCyclesController < Admin::ResourceController
class DateTimeChangeError < StandardError; end
include ::OrderCyclesHelper
include PaperTrailLogging
@@ -11,7 +13,6 @@ module Admin
before_action :remove_protected_attrs, only: [:update]
before_action :require_order_cycle_set_params, only: [:bulk_update]
around_action :protect_invalid_destroy, only: :destroy
before_action :verify_datetime_change, only: :update
def index
respond_to do |format|
@@ -63,9 +64,7 @@ module Admin
end
def update
@order_cycle_form = OrderCycles::FormService.new(@order_cycle, order_cycle_params,
spree_current_user)
@order_cycle_form = set_order_cycle_form
if @order_cycle_form.save
update_nil_subscription_line_items_price_estimate(@order_cycle)
respond_to do |format|
@@ -78,6 +77,9 @@ module Admin
elsif request.format.json?
render json: { errors: @order_cycle.errors.full_messages }, status: :unprocessable_entity
end
rescue DateTimeChangeError
render json: { trigger_action: params[:trigger_action] },
status: :unprocessable_entity
end
def bulk_update
@@ -91,6 +93,9 @@ module Admin
order_cycle = order_cycle_set.collection.find{ |oc| oc.errors.present? }
render json: { errors: order_cycle.errors.full_messages }, status: :unprocessable_entity
end
rescue DateTimeChangeError
render json: { trigger_action: params[:trigger_action] },
status: :unprocessable_entity
end
def bulk_update_nil_subscription_line_items_price_estimate
@@ -271,7 +276,10 @@ module Admin
end
def order_cycle_set
@order_cycle_set ||= Sets::OrderCycleSet.new(@order_cycles, order_cycle_bulk_params)
@order_cycle_set ||= Sets::OrderCycleSet.new(
@order_cycles, { **order_cycle_bulk_params,
confirm_datetime_change: params[:confirm], error_class: DateTimeChangeError }
)
end
def require_order_cycle_set_params
@@ -296,21 +304,13 @@ module Admin
).to_h.with_indifferent_access
end
# Check that order cycle datetime values changed if it has existing orders
def verify_datetime_change
return unless params[:order_cycle][:confirm]
return unless @order_cycle.orders.exists?
return if same_dates(@order_cycle.orders_open_at, order_cycle_params[:orders_open_at]) &&
same_dates(@order_cycle.orders_close_at, order_cycle_params[:orders_close_at])
render json: { trigger_action: params[:order_cycle][:trigger_action] },
status: :unprocessable_entity
end
def same_dates(date, string)
false unless date && string
DateTime.parse(string).to_fs(:short) == date.to_fs(:short)
def set_order_cycle_form
OrderCycles::FormService.new(
@order_cycle, order_cycle_params.merge(
{ confirm_datetime_change: params[:order_cycle][:confirm],
error_class: DateTimeChangeError }
), spree_current_user
)
end
end
end

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
module Admin
class ProductPreviewController < Spree::Admin::BaseController
def show
@product = Spree::Product.find(params[:id])
authorize! :show, @product
respond_with do |format|
format.turbo_stream {
render "admin/products_v3/product_preview", status: :ok
}
end
end
private
def model_class
Spree::Product
end
end
end

View File

@@ -40,6 +40,8 @@ module Admin
{ id: params[:id] }
).find_product
authorize! :delete, @record
@record.destroyed_by = spree_current_user
status = :ok
@@ -74,6 +76,8 @@ module Admin
def clone
@product = Spree::Product.find(params[:id])
authorize! :clone, @product
status = :ok
begin

View File

@@ -6,7 +6,7 @@ module Admin
include ReportsActions
helper ReportsHelper
before_action :authorize_report, only: [:show]
before_action :authorize_report, only: [:show, :create]
# Define model class for Can? permissions
def model_class
@@ -20,14 +20,17 @@ module Admin
end
def show
@report = report_class.new(spree_current_user, params, render: render_data?)
@rendering_options = rendering_options # also stores user preferences
@report = report_class.new(spree_current_user, params, render: false)
@rendering_options = rendering_options
if render_data?
render_in_background
else
show_report
end
show_report
end
def create
@report = report_class.new(spree_current_user, params, render: true)
update_rendering_options
render_in_background
end
private
@@ -54,31 +57,15 @@ module Admin
@variant_serialized = Api::Admin::VariantSerializer.new(variant)
end
def render_data?
request.post?
end
def render_in_background
cable_ready[ScopedChannel.for_id(params[:uuid])]
.inner_html(
selector: "#report-go",
html: helpers.button(t(:go), "report__submit-btn", "submit", disabled: true)
).inner_html(
selector: "#report-table",
html: render_to_string(partial: "admin/reports/loading")
).scroll_into_view(
selector: "#report-table",
block: "start"
).broadcast
@blob = ReportBlob.create_for_upload_later!(report_filename)
ReportJob.perform_later(
report_class:, user: spree_current_user, params:,
format: report_format,
filename: report_filename,
blob: @blob,
channel: ScopedChannel.for_id(params[:uuid]),
)
head :no_content
end
end
end

View File

@@ -88,14 +88,10 @@ module ReportsActions
display_header_row: false
}
end
update_rendering_options
@rendering_options
end
def update_rendering_options
return unless request.post?
@rendering_options.update(
rendering_options.update(
options: {
fields_to_show: params[:fields_to_show],
display_summary_row: params[:display_summary_row].present?,

View File

@@ -10,8 +10,11 @@ module Admin
end
end
def prepare_new_variant(product)
product.variants.build
def prepare_new_variant(product, producer_options)
# e.g producer_options = [['producer name', id]]
product.variants.build do |new_variant|
new_variant.supplier_id = producer_options.first.second if producer_options.one?
end
end
def unit_value_with_description(variant)
@@ -37,5 +40,11 @@ module Admin
"#{admin_products_path}#{url_filters.empty? ? '' : "#?#{url_filters.to_query}"}"
end
# if user hasn't saved any preferences on products page and there's only one producer;
# we need to hide producer column
def hide_producer_column?(producer_options)
spree_current_user.column_preferences.bulk_edit_product.empty? && producer_options.one?
end
end
end

View File

@@ -61,15 +61,6 @@ module ApplicationHelper
classes << shopfront_layout
end
def pdf_stylesheet_pack_tag(source)
if running_in_development?
options = { media: "all", host: "#{Webpacker.dev_server.host}:#{Webpacker.dev_server.port}" }
stylesheet_pack_tag(source, **options)
else
wicked_pdf_stylesheet_pack_tag(source)
end
end
def cache_with_locale(key = nil, options = {}, &block)
cache(cache_key_with_locale(key, I18n.locale), options) do
yield(block)

View File

@@ -8,11 +8,13 @@ module InjectionHelper
include OrderCyclesHelper
def inject_enterprises(enterprises = nil)
enterprises ||= default_enterprise_query
inject_json_array(
"enterprises",
enterprises || default_enterprise_query,
enterprises,
Api::EnterpriseSerializer,
enterprise_injection_data,
enterprise_injection_data(enterprises.map(&:id)),
)
end
@@ -57,15 +59,16 @@ module InjectionHelper
inject_json_array "enterprises",
enterprises_and_relatives,
Api::EnterpriseSerializer,
enterprise_injection_data
enterprise_injection_data(enterprises_and_relatives.map(&:id))
end
def inject_group_enterprises(group)
enterprises = group.enterprises.activated.visible.all
inject_json_array(
"enterprises",
group.enterprises.activated.visible.all,
enterprises,
Api::EnterpriseSerializer,
enterprise_injection_data,
enterprise_injection_data(enterprises.map(&:id)),
)
end
@@ -73,7 +76,7 @@ module InjectionHelper
inject_json "currentHub",
current_distributor,
Api::EnterpriseSerializer,
enterprise_injection_data
enterprise_injection_data(current_distributor ? [current_distributor.id] : nil)
end
def inject_current_order
@@ -153,7 +156,9 @@ module InjectionHelper
Enterprise.activated.includes(address: [:state, :country]).all
end
def enterprise_injection_data
@enterprise_injection_data ||= { data: OpenFoodNetwork::EnterpriseInjectionData.new }
def enterprise_injection_data(enterprise_ids)
{
data: OpenFoodNetwork::EnterpriseInjectionData.new(enterprise_ids)
}
end
end

View File

@@ -9,12 +9,12 @@ class ReportJob < ApplicationJob
NOTIFICATION_TIME = 5.seconds
def perform(report_class:, user:, params:, format:, filename:, channel: nil)
def perform(report_class:, user:, params:, format:, blob:, channel: nil)
start_time = Time.zone.now
report = report_class.new(user, params, render: true)
result = report.render_as(format)
blob = ReportBlob.create!(filename, result)
blob.store(result)
execution_time = Time.zone.now - start_time

View File

@@ -15,10 +15,11 @@ class ColumnPreference < ApplicationRecord
validates :column_name, presence: true, inclusion: { in: proc { |p|
valid_columns_for(p.action_name)
} }
scope :bulk_edit_product, -> { where(action_name: 'products_v3_index') }
def self.for(user, action_name)
stored_preferences = where(user_id: user.id, action_name:)
default_preferences = __send__("#{action_name}_columns")
default_preferences = get_default_preferences(action_name, user)
filter(default_preferences, user, action_name)
default_preferences.each_with_object([]) do |(column_name, default_attributes), preferences|
stored_preference = stored_preferences.find_by(column_name:)
@@ -36,7 +37,7 @@ class ColumnPreference < ApplicationRecord
end
def self.valid_columns_for(action_name)
__send__("#{action_name}_columns").keys.map(&:to_s)
get_default_preferences(action_name, Spree::User.new).keys.map(&:to_s)
end
def self.known_actions
@@ -52,4 +53,13 @@ class ColumnPreference < ApplicationRecord
default_preferences.delete(:schedules)
end
def self.get_default_preferences(action_name, user)
case action_name
when 'products_v3_index'
products_v3_index_columns(user)
else
__send__("#{action_name}_columns")
end
end
end

View File

@@ -6,14 +6,14 @@
# `count_on_hand` can either be: nil or a number
#
# This means that a variant override can be in six different stock states
# but only three of them are valid.
# but only four of them are valid.
#
# | on_demand | count_on_hand | stock_overridden? | use_producer_stock_settings? | valid? |
# |-----------|---------------|-------------------|------------------------------|--------|
# | 1 | nil | false | false | true |
# | 1 | nil | true | false | true |
# | 0 | x | true | false | true |
# | nil | nil | false | true | true |
# | 1 | x | ? | ? | false |
# | 1 | x | true | false | true |
# | 0 | nil | ? | ? | false |
# | nil | x | ? | ? | false |
#
@@ -27,7 +27,6 @@ module StockSettingsOverrideValidation
def require_compatible_on_demand_and_count_on_hand
disallow_count_on_hand_if_using_producer_stock_settings
disallow_count_on_hand_if_on_demand
require_count_on_hand_if_limited_stock
end
@@ -39,14 +38,6 @@ module StockSettingsOverrideValidation
errors.add(:count_on_hand, error_message)
end
def disallow_count_on_hand_if_on_demand
return unless on_demand? && count_on_hand.present?
error_message = I18n.t("count_on_hand.on_demand_but_count_on_hand_set",
scope: i18n_scope_for_stock_settings_override_validation_error)
errors.add(:count_on_hand, error_message)
end
def require_count_on_hand_if_limited_stock
return unless on_demand == false && count_on_hand.blank?

View File

@@ -96,7 +96,7 @@ module VariantStock
# Here we depend only on variant.total_on_hand and variant.on_demand.
# This way, variant_overrides only need to override variant.total_on_hand and variant.on_demand.
def fill_status(quantity)
on_hand = if total_on_hand >= quantity || on_demand
on_hand = if total_on_hand.to_i >= quantity || on_demand
quantity
else
[0, total_on_hand].max
@@ -112,8 +112,7 @@ module VariantStock
#
# This enables us to override this behaviour for variant overrides
def move(quantity, originator = nil)
# Don't change variant stock if variant is on_demand or has been deleted
return if on_demand || deleted_at
return if deleted_at
raise_error_if_no_stock_item_available

View File

@@ -1,7 +1,5 @@
# frozen_string_literal: false
require "mini_magick"
class Enterprise < ApplicationRecord
SELLS = %w(unspecified none own any).freeze
ENTERPRISE_SEARCH_RADIUS = 100
@@ -249,11 +247,6 @@ class Enterprise < ApplicationRecord
count(distinct: true)
end
# Remove any unsupported HTML.
def long_description
HtmlSanitizer.sanitize(super)
end
# Remove any unsupported HTML.
def long_description=(html)
super(HtmlSanitizer.sanitize(html))
@@ -479,7 +472,7 @@ class Enterprise < ApplicationRecord
return unless image.variable?
image_variant_url_for(image.variant(name))
rescue ActiveStorage::Error, MiniMagick::Error, ActionView::Template::Error => e
rescue StandardError => e
Bugsnag.notify "Enterprise ##{id} #{image.try(:name)} error: #{e.message}"
Rails.logger.error(e.message)

View File

@@ -148,17 +148,20 @@ class OrderCycle < ApplicationRecord
# Find the earliest closing times for each distributor in an active order cycle, and return
# them in the format {distributor_id => closing_time, ...}
def self.earliest_closing_times
Hash[
Exchange.
outgoing.
joins(:order_cycle).
merge(OrderCycle.active).
group('exchanges.receiver_id').
select("exchanges.receiver_id AS receiver_id,
MIN(order_cycles.orders_close_at) AS earliest_close_at").
map { |ex| [ex.receiver_id, ex.earliest_close_at.to_time] }
]
#
# Optionally, specify some distributor_ids as a parameter to scope the results
def self.earliest_closing_times(distributor_ids = nil)
cycles = Exchange.
outgoing.
joins(:order_cycle).
merge(OrderCycle.active).
group('exchanges.receiver_id')
cycles = cycles.where(receiver_id: distributor_ids) if distributor_ids.present?
cycles.pluck("exchanges.receiver_id AS receiver_id",
"MIN(order_cycles.orders_close_at) AS earliest_close_at")
.to_h
end
def attachable_distributor_payment_methods
@@ -314,6 +317,13 @@ class OrderCycle < ApplicationRecord
coordinator.sells == 'own'
end
def same_datetime_value(attribute, string)
return true if self[attribute].blank? && string.blank?
return false if self[attribute].blank? || string.blank?
DateTime.parse(string).to_fs(:short) == self[attribute]&.to_fs(:short)
end
private
def opening?

View File

@@ -5,7 +5,7 @@ class ReportBlob < ActiveStorage::Blob
# AWS S3 limits URL expiry to one week.
LIFETIME = 1.week
def self.create!(filename, content)
def self.create_locally!(filename, content)
create_and_upload!(
io: StringIO.new(content),
filename:,
@@ -15,11 +15,34 @@ class ReportBlob < ActiveStorage::Blob
)
end
def self.create_for_upload_later!(filename)
# ActiveStorage discourages modifying a blob later but we need a blob
# before we know anything about the report file. It enables us to use the
# same blob in the controller to read the result.
create_before_direct_upload!(
filename:,
byte_size: 0,
checksum: "0",
content_type: content_type(filename),
service_name: :local,
).tap do |blob|
ActiveStorage::PurgeJob.set(wait: LIFETIME).perform_later(blob)
end
end
def self.content_type(filename)
MIME::Types.of(filename).first&.to_s || "application/octet-stream"
end
def store(content)
io = StringIO.new(content)
upload(io, identify: false)
save!
end
def result
return if checksum == "0"
@result ||= download.force_encoding(Encoding::UTF_8)
end

View File

@@ -29,7 +29,6 @@ module Spree
can :update, Order do |order, token|
order.user == user || (order.token && token == order.token)
end
can [:index, :read], Product
can [:index, :read], ProductProperty
can [:index, :read], Property
can :create, Spree::User
@@ -243,8 +242,8 @@ module Spree
can [:admin, :index], ::Admin::DfcProductImportsController
# Reports page
can [:admin, :index, :show], ::Admin::ReportsController
can [:admin, :show, :customers, :orders_and_distributors, :group_buys, :payments,
can [:admin, :index, :show, :create], ::Admin::ReportsController
can [:admin, :show, :create, :customers, :orders_and_distributors, :group_buys, :payments,
:orders_and_fulfillment, :products_and_inventory, :order_cycle_management,
:packing, :enterprise_fee_summary, :bulk_coop], :report
end
@@ -324,7 +323,7 @@ module Spree
end
# Reports page
can [:admin, :index, :show], ::Admin::ReportsController
can [:admin, :index, :show, :create], ::Admin::ReportsController
can [:admin, :customers, :group_buys, :sales_tax, :payments,
:orders_and_distributors, :orders_and_fulfillment, :products_and_inventory,
:order_cycle_management, :xero_invoices, :enterprise_fee_summary, :bulk_coop], :report

View File

@@ -1,7 +1,5 @@
# frozen_string_literal: true
require "mini_magick"
module Spree
class Image < Asset
has_one_attached :attachment, service: image_service do |attachment|
@@ -35,7 +33,7 @@ module Spree
return self.class.default_image_url(size) unless attachment.attached?
image_variant_url_for(variant(size))
rescue ActiveStorage::Error, MiniMagick::Error, ActionView::Template::Error => e
rescue StandardError => e
Bugsnag.notify "Product ##{viewable_id} Image ##{id} error: #{e.message}"
Rails.logger.error(e.message)

View File

@@ -263,10 +263,12 @@ module Spree
# Format as per WeightsAndMeasures (todo: re-orgnaise maybe after product/variant refactor)
def variant_unit_with_scale
# Our code is based upon English based number formatting with a period `.`
scale_clean = ActiveSupport::NumberHelper.number_to_rounded(variant_unit_scale,
precision: nil,
significant: false,
strip_insignificant_zeros: true)
strip_insignificant_zeros: true,
locale: :en)
[variant_unit, scale_clean].compact_blank.join("_")
end
@@ -295,14 +297,8 @@ module Spree
# eg clone product. Will raise error if clonning a product with no variant
return if variants.first&.valid?
unless Spree::Taxon.find_by(id: primary_taxon_id)
errors.add(:primary_taxon_id,
I18n.t('activerecord.errors.models.spree/product.must_exist'))
end
return if Enterprise.find_by(id: supplier_id)
errors.add(:supplier_id,
I18n.t('activerecord.errors.models.spree/product.must_exist'))
errors.add(:primary_taxon_id, :blank) unless Spree::Taxon.find_by(id: primary_taxon_id)
errors.add(:supplier_id, :blank) unless Enterprise.find_by(id: supplier_id)
end
def update_units

View File

@@ -87,16 +87,20 @@ module Spree
# Return the services (pickup, delivery) that different distributors provide, in the format:
# {distributor_id => {pickup: true, delivery: false}, ...}
def self.services
Hash[
Spree::ShippingMethod.
joins(:distributor_shipping_methods).
group('distributor_id').
select("distributor_id").
select("BOOL_OR(spree_shipping_methods.require_ship_address = 'f') AS pickup").
select("BOOL_OR(spree_shipping_methods.require_ship_address = 't') AS delivery").
map { |sm| [sm.distributor_id.to_i, { pickup: sm.pickup, delivery: sm.delivery }] }
]
#
# Optionally, specify some distributor_ids as a parameter to scope the results
def self.services(distributor_ids = nil)
methods = Spree::ShippingMethod.joins(:distributor_shipping_methods).group('distributor_id')
if distributor_ids.present?
methods = methods.where(distributor_shipping_methods: { distributor_id: distributor_ids })
end
methods.
pluck(Arel.sql("distributor_id"),
Arel.sql("BOOL_OR(spree_shipping_methods.require_ship_address = 'f') AS pickup"),
Arel.sql("BOOL_OR(spree_shipping_methods.require_ship_address = 't') AS delivery")).
to_h { |(distributor_id, pickup, delivery)| [distributor_id.to_i, { pickup:, delivery: }] }
end
def self.backend

View File

@@ -25,18 +25,19 @@ module Spree
# Find all the taxons of supplied products for each enterprise, indexed by enterprise.
# Format: {enterprise_id => [taxon_id, ...]}
def self.supplied_taxons
taxons = {}
#
# Optionally, specify some enterprise_ids to scope the results
def self.supplied_taxons(enterprise_ids = nil)
taxons = Spree::Taxon.joins(variants: :supplier)
Spree::Taxon.
joins(variants: :supplier).
select('spree_taxons.*, enterprises.id AS enterprise_id').
each do |t|
taxons[t.enterprise_id.to_i] ||= Set.new
taxons[t.enterprise_id.to_i] << t.id
end
taxons = taxons.where(enterprises: { id: enterprise_ids }) if enterprise_ids.present?
taxons
.pluck('spree_taxons.id, enterprises.id AS enterprise_id')
.each_with_object({}) do |(taxon_id, enterprise_id), collection|
collection[enterprise_id.to_i] ||= Set.new
collection[enterprise_id.to_i] << taxon_id
end
end
# Find all the taxons of distributed products for each enterprise, indexed by enterprise.
@@ -44,7 +45,9 @@ module Spree
# or :current taxons (distributed in an open order cycle).
#
# Format: {enterprise_id => [taxon_id, ...]}
def self.distributed_taxons(which_taxons = :all)
#
# Optionally, specify some enterprise_ids to scope the results
def self.distributed_taxons(which_taxons = :all, enterprise_ids = nil)
ents_and_vars = ExchangeVariant.joins(exchange: :order_cycle).merge(Exchange.outgoing)
.select("DISTINCT variant_id, receiver_id AS enterprise_id")
@@ -57,6 +60,10 @@ module Spree
INNER JOIN (#{ents_and_vars.to_sql}) AS ents_and_vars
ON spree_variants.id = ents_and_vars.variant_id")
if enterprise_ids.present?
taxons = taxons.where(ents_and_vars: { enterprise_id: enterprise_ids })
end
taxons.each_with_object({}) do |t, ts|
ts[t.enterprise_id.to_i] ||= Set.new
ts[t.enterprise_id.to_i] << t.id

View File

@@ -42,6 +42,7 @@ module Spree
has_many :credit_cards, dependent: :destroy
has_many :report_rendering_options, class_name: "::ReportRenderingOptions", dependent: :destroy
has_many :webhook_endpoints, dependent: :destroy
has_many :column_preferences, dependent: :destroy
has_one :oidc_account, dependent: :destroy
accepts_nested_attributes_for :enterprise_roles, allow_destroy: true

View File

@@ -15,7 +15,9 @@ class VariantOverride < ApplicationRecord
# Need to ensure this can be set by the user.
validates :default_stock, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
validates :price, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
validates :count_on_hand, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
validates :count_on_hand, numericality: {
greater_than_or_equal_to: 0, unless: :on_demand?
}, allow_nil: true
default_scope { where(permission_revoked_at: nil) }
@@ -36,9 +38,8 @@ class VariantOverride < ApplicationRecord
end
def stock_overridden?
# If count_on_hand is present, it means on_demand is false
# See StockSettingsOverrideValidation for details
count_on_hand.present?
# Testing for not nil because for a boolean `false.present?` is false.
!on_demand.nil? || !count_on_hand.nil?
end
def use_producer_stock_settings?

View File

@@ -7,7 +7,7 @@ module Api
attributes :orders_close_at, :active
def orders_close_at
options[:data].earliest_closing_times[object.id]
options[:data].earliest_closing_times[object.id]&.to_time
end
def active

View File

@@ -7,6 +7,8 @@ module OrderCycles
class FormService
def initialize(order_cycle, order_cycle_params, user)
@order_cycle = order_cycle
@confirm_datetime_change = order_cycle_params.delete :confirm_datetime_change
@error_class = order_cycle_params.delete :error_class
@order_cycle_params = order_cycle_params
@specified_params = order_cycle_params.keys
@user = user
@@ -21,6 +23,9 @@ module OrderCycles
end
def save
# Check that order cycle datetime values changed if it has existing orders
verify_datetime_change!
schedule_ids = build_schedule_ids
order_cycle.assign_attributes(order_cycle_params)
return false unless order_cycle.valid?
@@ -229,5 +234,16 @@ module OrderCycles
DistributorShippingMethod.where(distributor_id: user_distributors_ids)
.pluck(:id)
end
def verify_datetime_change!
return unless @confirm_datetime_change
return unless @order_cycle.orders.exists?
return if @order_cycle.same_datetime_value(:orders_open_at,
@order_cycle_params[:orders_open_at]) &&
@order_cycle.same_datetime_value(:orders_close_at,
@order_cycle_params[:orders_close_at])
raise @error_class
end
end
end

View File

@@ -9,7 +9,7 @@ module PermittedAttributes
:unit_description, :variant_unit_name,
:display_as, :sku, :group_buy, :group_buy_unit_size,
:taxon_ids, :primary_taxon_id, :tax_category_id, :supplier_id,
:meta_keywords, :notes, :inherits_properties,
:meta_keywords, :notes, :inherits_properties, :shipping_category_id,
{ product_properties_attributes: [:id, :property_name, :value],
variants_attributes: [PermittedAttributes::Variant.attributes],
image_attributes: [:attachment] }

View File

@@ -3,7 +3,31 @@
module Sets
class OrderCycleSet < ModelSet
def initialize(collection, attributes = {})
@confirm_datetime_change = attributes.delete :confirm_datetime_change
@error_class = attributes.delete :error_class
super(OrderCycle, collection, attributes)
end
def process(order_cycle, attributes)
if @confirm_datetime_change &&
order_cycle.orders.exists? &&
datetime_value_changed(order_cycle, attributes)
raise @error_class
end
super
end
private
def datetime_value_changed(order_cycle, attributes)
# return true if either key is present in params and change in values detected
return true if attributes.key?(:orders_open_at) &&
!order_cycle.same_datetime_value(:orders_open_at, attributes[:orders_open_at])
attributes.key?(:orders_close_at) &&
!order_cycle.same_datetime_value(:orders_close_at, attributes[:orders_close_at])
end
end
end

View File

@@ -1,12 +1,17 @@
# frozen_string_literal: true
class ShopsListService
# shops that are ready for checkout, and have an order cycle that is currently open
def open_shops
shops_list.ready_for_checkout.all
shops_list.
ready_for_checkout.
distributors_with_active_order_cycles
end
# shops that are either not ready for checkout, or don't have an open order cycle; the inverse of
# #open_shops
def closed_shops
shops_list.not_ready_for_checkout.all
shops_list.where.not(id: open_shops.reselect("enterprises.id"))
end
private

View File

@@ -26,9 +26,15 @@ class WeightsAndMeasures
def self.variant_unit_options
available_units_sorted.flat_map do |measurement, measurement_info|
measurement_info.filter_map do |scale, unit_info|
# Our code is based upon English based number formatting
# Some language locales like +hu+ uses a comma(,) for decimal separator
# While in English, decimal separator is represented by a period.
# e.g. en: 0.001, hu: 0,001
# Hence the results become "weight_0,001" for hu while or code recognizes "weight_0.001"
scale_clean =
ActiveSupport::NumberHelper.number_to_rounded(scale, precision: nil, significant: false,
strip_insignificant_zeros: true)
strip_insignificant_zeros: true,
locale: :en)
[
"#{I18n.t(measurement)} (#{unit_info['name']})", # Label (eg "Weight (g)")
"#{measurement}_#{scale_clean}", # Scale ID (eg "weight_1")

View File

@@ -4,11 +4,18 @@
%div{ style: 'font-size: 1rem;' }
= t('.content')
%p.modal-actions.justify-end.gap-1
%button.button.secondary{ "ng-click": "submit($event, null)", type: "button", style: "display: none;", data: { action: 'click->modal#close', 'trigger-action': 'save' } }
= t('.proceed')
%button.button.secondary{ "ng-click": "submit($event, '#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", type: "button", style: "display: none;", data: { action: 'click->modal#close', 'trigger-action': 'saveAndNext' } }
= t('.proceed')
%button.button.secondary{ "ng-click": "submit($event, '#{main_app.admin_order_cycles_path}')", type: "button", style: "display: none;", data: { action: 'click->modal#close', 'trigger-action': 'saveAndBack' } }
= t('.proceed')
%button.button.primary{ type: "button", 'data-action': 'click->modal#close' }
= t('.cancel')
- if action == 'simple_update'
%button.button.secondary{ "ng-click": "submit($event, null)", type: "button", style: "display: none;", data: { action: 'click->modal#close', 'trigger-action': 'save' } }
= t('.proceed')
%button.button.secondary{ "ng-click": "submit($event, '#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", type: "button", style: "display: none;", data: { action: 'click->modal#close', 'trigger-action': 'saveAndNext' } }
= t('.proceed')
%button.button.secondary{ "ng-click": "submit($event, '#{main_app.admin_order_cycles_path}')", type: "button", style: "display: none;", data: { action: 'click->modal#close', 'trigger-action': 'saveAndBack' } }
= t('.proceed')
%button.button.primary{ type: "button", 'data-action': 'click->modal#close' }
= t('.cancel')
- if action == 'bulk_update'
%button.button.secondary{ "ng-click": "saveAll($event)", type: "button", style: "display: none;", data: { action: 'click->modal#close', trigger_action: 'bulk_save' } }
= t('.proceed')
%button.button.primary{ type: "button", 'data-action': 'click->modal#close' }
= t('.cancel')

View File

@@ -37,4 +37,4 @@
%button.modal-target-trigger{ type: 'button', data: { 'action': 'modal-link#open' }, style: 'display: none;' }
= render ModalComponent.new(id: "linked-order-warning-modal", close_button: false) do
.content.flex-column.gap-2
= render 'date_time_warning_modal_content'
= render 'date_time_warning_modal_content', action: 'simple_update'

View File

@@ -23,12 +23,20 @@
.thirteen.columns.alpha &nbsp;
%columns-dropdown{ action: "#{controller_name}_#{action_name}" }
%form{ name: 'order_cycles_form' }
%save-bar{ dirty: "order_cycles_form.$dirty", persist: "false" }
%input.red{ type: "button", value: t(:save_changes), "ng-click": "saveAll()", "ng-disabled": "!order_cycles_form.$dirty" }
%table.index#listing_order_cycles{ "ng-show": 'orderCycles.length > 0' }
= render 'admin/order_cycles/header' #, simple_index: simple_index
%tbody
= render 'admin/order_cycles/row' #, simple_index: simple_index
= render 'admin/order_cycles/loading_flash'
= render 'admin/order_cycles/show_more'
%div{ data: { controller: 'order-cycle-form' } }
%form{ name: 'order_cycles_form' }
%save-bar{ dirty: "order_cycles_form.$dirty", persist: "false" }
%input.red{ type: "button", value: t(:save_changes), "ng-click": "saveAll($event)", "ng-disabled": "!order_cycles_form.$dirty",
data: { confirm: "true", 'trigger-action': 'bulk_save' } }
%table.index#listing_order_cycles{ "ng-show": 'orderCycles.length > 0' }
= render 'admin/order_cycles/header' #, simple_index: simple_index
%tbody
= render 'admin/order_cycles/row' #, simple_index: simple_index
= render 'admin/order_cycles/loading_flash'
= render 'admin/order_cycles/show_more'
%div.warning-modal{ data: { controller: 'modal modal-link', 'modal-link-target-value': "linked-order-warning-modal" } }
%button.modal-target-trigger{ type: 'button', data: { 'action': 'modal-link#open' }, style: 'display: none;' }
= render ModalComponent.new(id: "linked-order-warning-modal", close_button: false) do
.content.flex-column.gap-2
= render 'date_time_warning_modal_content', action: 'bulk_update'

View File

@@ -25,7 +25,7 @@
-# empty
%td.col-on_hand.align-right
-# empty
%td.col-on_hand.align-right
%td.col-producer.align-right
-# empty
%td.col-category.align-left
-# empty
@@ -40,3 +40,4 @@
"data-modal-link-target-value": "product-delete-modal", "class": "delete",
"data-modal-link-modal-dataset-value": {'data-delete-path': admin_product_destroy_path(product)}.to_json }
= t('admin.products_page.actions.delete')
= link_to t('admin.products_page.actions.preview'), admin_product_preview_path(product), {"data-turbo-stream": "" }

View File

@@ -11,7 +11,7 @@
%tr.condensed{ id: dom_id(variant), 'data-controller': "variant", 'class': "nested-form-wrapper", 'data-new-record': variant.new_record? ? "true" : false }
= render partial: 'variant_row', locals: { variant:, f: variant_form, category_options:, tax_category_options:, producer_options: }
= form.fields_for("products][#{product_index}][variants_attributes][NEW_RECORD", prepare_new_variant(product)) do |new_variant_form|
= form.fields_for("products][#{product_index}][variants_attributes][NEW_RECORD", prepare_new_variant(product, producer_options)) do |new_variant_form|
%template{ 'data-nested-form-target': "template" }
%tr.condensed{ 'data-controller': "variant", 'class': "nested-form-wrapper", 'data-new-record': "true" }
= render partial: 'variant_row', locals: { variant: new_variant_form.object, f: new_variant_form, category_options:, tax_category_options:, producer_options: }

View File

@@ -1,7 +1,7 @@
#sort
%div.pagination-description
- if pagy.present?
= t(".pagination.total_html", total: pagy.count, from: pagy.from, to: pagy.to)
= t(".pagination.products_total_html", count: pagy.count, from: pagy.from, to: pagy.to)
- if search_term.present? || producer_id.present? || category_id.present?
%a{ href: url_for(page: 1), class: "button disruptive", data: { 'turbo-frame': "_self", 'turbo-action': "advance" } }

View File

@@ -13,16 +13,16 @@
= hidden_field_tag :producer_id, @producer_id
= hidden_field_tag :category_id, @category_id
%table.products{ 'data-column-preferences-target': "table" }
%table.products{ 'data-column-preferences-target': "table", class: (hide_producer_column?(producer_options) ? 'hide-producer' : '') }
%colgroup
-# The `min-width` property works in Chrome but not Firefox so is considered progressive enhancement.
%col.col-image{ width:"56px" }= # (image size + padding)
%col.col-image{ width:"44px" }= # (image size + padding)
%col.col-name{ style:"min-width: 6em" }= # (grow to fill)
%col.col-sku{ width:"8%", style:"min-width: 6em" }
%col.col-unit_scale{ width:"8%" }
%col.col-unit{ width:"8%" }
%col.col-price{ width:"5%", style:"min-width: 5em" }
%col.col-on_hand{ width:"10%"}
%col.col-price{ width:"10%", style:"min-width: 5em" }
%col.col-on_hand{ width:"8%" }
%col.col-producer{ style:"min-width: 6em" }= # (grow to fill)
%col.col-category{ width:"8%" }
%col.col-tax_category{ width:"8%" }

View File

@@ -20,3 +20,4 @@
= render partial: 'delete_modal', locals: { object_type: }
#modal-component
#edit_image_modal
#product-preview-modal-container

View File

@@ -0,0 +1,119 @@
= turbo_stream.update "product-preview-modal-container" do
= render ModalComponent.new(id: "product-preview-modal", instant: true, modal_class: "big") do
#product-preview{ "data-controller": "tabs" }
%h1
= t("admin.products_page.product_preview.product_preview")
%dl.admin-tabs
%dd
%a{ data: { "tabs-target": "tab", "action": "tabs#select" } }
= t("admin.products_page.product_preview.shop_tab")
%dd
%a{ data: { "tabs-target": "tab", "action": "tabs#select" } }
= t("admin.products_page.product_preview.product_details_tab")
.tabs-content
.content.active
%div{ data: { "tabs-target": "content" } }
.product-thumb
%a
- if @product.group_buy
%span.product-thumb__bulk-label
= t(".bulk")
= image_tag @product.image&.url(:small) || Spree::Image.default_image_url(:small)
.summary
.summary-header
%h3
%a
%span
= @product.name
- if @product.description
.product-description{ "data-controller": "add-blank-to-link" }
- # description is sanitized in Spree::Product#description method
= @product.description.html_safe
- if @product.variants.first.supplier.visible
%div
.product-producer
= t :products_from
%span
%a
= @product.variants.first.supplier.name
.product-properties.filter-shopfront.property-selectors
.filter-shopfront.property-selectors.inline-block
%ul
- @product.properties_including_inherited.each do |property|
%li
- if property[:value].present?
= render AdminTooltipComponent.new(text: property[:value], link_text: property[:name], placement: "bottom")
- else
%a
%span
= property[:name]
.shop-variants
- @product.variants.sort { |v1, v2| v1.name_to_display <=> v2.name_to_display }.sort { |v1, v2| v1.unit_value <=> v2.unit_value }.each do |variant|
.variants.row
.small-3.columns.variant-name
- if variant.display_name.present?
.inline
= variant.display_name
.variant-unit
= variant.unit_to_display
.small-4.medium-3.columns.variant-price
= number_to_currency(variant.price)
.unit-price.variant-unit-price
= render AdminTooltipComponent.new(text: t("js.shopfront.unit_price_tooltip"), link_text: "", placement: "top", link_class: "question-mark-icon")
- # TODO use an helper
- unit_price = UnitPrice.new(variant)
- price_per_unit = variant.price / (unit_price.denominator || 1)
= "#{number_to_currency(price_per_unit)}&nbsp;/&nbsp;#{unit_price.unit}".html_safe
.medium-3.columns.total-price
%span
= number_to_currency(0.00)
.small-5.medium-3.large-3.columns.variant-quantity-column.text-right
.variant-quantity-inputs
%button.add-variant
= t("js.shopfront.variant.add_to_cart")
- # TODO can't check the shop preferrence here, display by default ?
- if !variant.on_demand && variant.on_hand <= 3
.variant-remaining-stock
= t("js.shopfront.variant.remaining_in_stock", quantity: variant.on_hand)
%div{ data: { "tabs-target": "content" } }
.row
.columns.small-12.medium-6.large-6.product-header
%h3
= @product.name
%span
%em
= t("products_from")
%span
= @product.variants.first.supplier.name
%br
.filter-shopfront.property-selectors.inline-block
%ul
- @product.properties_including_inherited.each do |property|
%li
- if property[:value].present?
= render AdminTooltipComponent.new(text: property[:value], link_text: property[:name], placement: "bottom")
- else
%a
%span
= property[:name]
- if @product.description
.product-description{ 'data-controller': "add-blank-to-link" }
%p.text-small
- # description is sanitized in Spree::Product#description method
= @product.description.html_safe
.columns.small-12.medium-6.large-6.product-img
- if @product.image
%img{ src: @product.image.url(:large) }
-else
%img.placeholder{ src: Spree::Image.default_image_url(:large) }

View File

@@ -0,0 +1,47 @@
.download.hidden
= link_to t("admin.reports.download.button"), file_url, target: "_blank", class: "button icon icon-file"
:javascript
(function () {
const tryDownload = function() {
const link = document.querySelector(".download a");
// If the report was already rendered via web sockets:
if (link == null) return;
fetch(link.href).then((response) => {
if (response.ok) {
response.blob().then((blob) => blob.text()).then((text) => {
const loading = document.querySelector(".loading");
if (loading == null) return;
loading.remove();
document.querySelector("#report-go button").disabled = false;
if (link.href.endsWith(".html")) {
// This replaces the hidden download button with the report:
link.parentElement.outerHTML = text;
} else {
// Or just show the download button when it's ready:
document.querySelector(".download").classList.remove("hidden")
}
});
} else {
setTimeout(tryDownload, 2000);
}
});
}
/*
A lot of reports are rendered within 250ms. Others take at least
2.5 seconds. There's a big gap in between. Observed on:
https://openfoodnetwork.org.au/admin/sidekiq/metrics/ReportJob?period=8h
https://openfoodnetwork.org.uk/admin/sidekiq/metrics/ReportJob?period=8h
https://coopcircuits.fr/admin/sidekiq/metrics/ReportJob?period=8h
But let's leave the timed response to websockets for now and just poll
as a backup mechanism.
*/
setTimeout(tryDownload, 3000);
})();

View File

@@ -0,0 +1,6 @@
= turbo_stream.update "report-go" do
= button t(:go), "report__submit-btn", "submit", disabled: true
= turbo_stream.update "report-table" do
= render "admin/reports/loading"
= render "admin/reports/fallback_display", file_url: @blob.expiring_service_url
= turbo_stream.scroll_into_view("#report-table", behavior: "smooth")

View File

@@ -3,7 +3,7 @@
- content_for :minimal_js, true
= form_for @report.search, { url: url_for, data: { remote: "true" } } do |f|
= form_for @report.search, { url: url_for, data: { turbo: "true" } } do |f|
= hidden_field_tag "uuid", request.uuid
%fieldset.no-border-bottom.print-hidden

View File

@@ -1,7 +0,0 @@
%div{"data-controller": "tooltip"}
%a{"data-tooltip-target": "element", href: link, class: link_class}
= link_text
.tooltip-container
.tooltip{"data-tooltip-target": "tooltip"}
= sanitize tooltip_text
.arrow{"data-tooltip-target": "arrow"}

View File

@@ -1 +1 @@
= render partial: 'admin/shared/tooltip', locals: {link_class: "" ,link: nil, link_text: t('admin.whats_this'), tooltip_text: tooltip_text}
= render AdminTooltipComponent.new(text: tooltip_text, link_text: t('admin.whats_this'), link: nil)

View File

@@ -5,7 +5,7 @@
%meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
%title
= Spree::Config[:site_name]
= stylesheet_pack_tag 'mail'
= stylesheet_link_tag 'mail'
%body{:bgcolor => "#FFFFFF" }
- unless @hide_ofn_navigation
%table.head-wrap{:bgcolor => "#f2f2f2"}

View File

@@ -1,3 +1,4 @@
- # NOTE: make sure that any changes in this template are reflected in app/views/admin/products_v3/product_preview.turbo_stream.haml
= cache_with_locale do
%form{action: main_app.cart_path}
%products{"ng-init" => "refreshStaleData()", "ng-show" => "order_cycle.order_cycle_id != null", "ng-cloak" => true }

View File

@@ -1,8 +1,9 @@
- # NOTE: make sure that any changes in this template are reflected in app/views/admin/products_v3/product_preview.turbo_stream.haml
= cache_with_locale do
.small-4.medium-4.large-5.columns.variant-name
.small-3.columns.variant-name
.inline{"ng-if" => "::variant.display_name"} {{ ::variant.display_name }}
.variant-unit {{ ::variant.unit_to_display }}
.small-3.medium-3.large-2.columns.variant-price
.small-4.medium-3.columns.variant-price
%price-breakdown{"price-breakdown" => "_", variant: "variant",
"price-breakdown-append-to-body" => "true",
"price-breakdown-placement" => "bottom",
@@ -16,7 +17,7 @@
key: "'js.shopfront.unit_price_tooltip'"}
{{ variant.unit_price_price | localizeCurrency }}&nbsp;/&nbsp;{{ variant.unit_price_unit }}
.medium-2.large-2.columns.total-price
.medium-3.columns.total-price
%span{"ng-class" => "{filled: variant.line_item.total_price}"}
{{ variant.line_item.total_price | localizeCurrency }}
= render partial: "shop/products/shop_variant_no_group_buy"

View File

@@ -1,3 +1,4 @@
- # NOTE: make sure that any changes in this template are reflected in app/views/admin/products_v3/product_preview.turbo_stream.haml
= cache_with_locale do
.small-5.medium-3.large-3.columns.variant-quantity-column.text-right{"ng-if" => "::!variant.product.group_buy"}

View File

@@ -1,3 +1,4 @@
- # NOTE: make sure that any changes in this template are reflected in app/views/admin/products_v3/product_preview.turbo_stream.haml
= cache_with_locale do
.small-5.medium-3.large-3.columns.variant-quantity-column.text-right{"ng-if" => "::variant.product.group_buy"}

View File

@@ -1,3 +1,4 @@
- # NOTE: make sure that any changes in this template are reflected in app/views/admin/products_v3/product_preview.turbo_stream.haml
= cache_with_locale do
.product-thumb
%a{"ng-click" => "triggerProductModal()"}

View File

@@ -45,7 +45,7 @@
%div.row-loading-icons
- if local_assigns[:success]
%i.success.icon-ok-sign{"data-controller": "ephemeral"}
= render partial: 'admin/shared/tooltip', locals: {link_class: "icon_link with-tip icon-edit no-text" ,link: edit_admin_order_path(order), link_text: "", tooltip_text: t('spree.admin.orders.index.edit')}
= render AdminTooltipComponent.new(text: t('spree.admin.orders.index.edit'), link_text: "", link: edit_admin_order_path(order), link_class: "icon_link with-tip icon-edit no-text")
- if order.ready_to_ship?
%form
= render ShipOrderComponent.new(order: order)

View File

@@ -1,4 +1,4 @@
= pdf_stylesheet_pack_tag "mail"
= wicked_pdf_stylesheet_link_tag "mail"
%table{:width => "100%"}
%tbody

View File

@@ -1,4 +1,4 @@
= pdf_stylesheet_pack_tag "mail"
= wicked_pdf_stylesheet_link_tag "mail"
%table{:width => "100%"}
%tbody

View File

@@ -1,4 +1,4 @@
= pdf_stylesheet_pack_tag "mail"
= wicked_pdf_stylesheet_link_tag "mail"
%table{:width => "100%"}
%tbody

View File

@@ -1,6 +1,12 @@
= f.field_container :primary_taxon do
= f.field_container :primary_taxon_id do
= f.label :primary_taxon_id, t('.product_category')
%span.required *
%br
= f.collection_select(:primary_taxon_id, Spree::Taxon.order(:name), :id, :name, {:include_blank => true}, {:class => "select2 fullwidth"})
= render(SearchableDropdownComponent.new(form: f,
name: :primary_taxon_id,
aria_label: t('.product_category'),
options: Spree::Taxon.select(:name, :id).order(:name).pluck(:name, :id),
selected_option: @product.primary_taxon_id,
include_blank: true,
placeholder_value: t('.search_for_categories')))
= f.error_message_on :primary_taxon_id

View File

@@ -1,4 +1,4 @@
= f.field_container :shipping_categories do
= f.field_container :shipping_category_id do
= f.label :shipping_category_id, t(:shipping_category)
= f.collection_select(:shipping_category_id, Spree::ShippingCategory.all, :id, :name, {:include_blank => false}, {:class => 'select2 fullwidth'})
= f.error_message_on :shipping_category_id

View File

@@ -11,9 +11,13 @@
= render :partial => 'spree/shared/error_messages', :locals => { :target => @product }
= form_for [:admin, @product], :url => admin_product_path(@product, @url_filters), :method => :put, :html => { :multipart => true } do |f|
%fieldset.no-border-top{'ng-app' => 'admin.products'}
%fieldset.no-border-top{'ng-app': 'admin.products', 'data-turbo': true, 'data-controller': "product-preview"}
= render :partial => 'form', :locals => { :f => f }
.form-buttons.filter-actions.actions
= link_to t("admin.products_page.actions.preview"), Rails.application.routes.url_helpers.admin_product_preview_path(@product), {"data-turbo-stream": "" , class: "button secondary"}
= button t(:update), 'icon-refresh'
= button_link_to t(:cancel), products_return_to_url(@url_filters), icon: 'icon-remove'
#product-preview-modal-container

View File

@@ -8,10 +8,16 @@
%legend{align: "center"}= t(".new_product")
.sixteen.columns.alpha
.eight.columns.alpha
= f.field_container :supplier do
= f.label :supplier, t(".supplier")
= f.field_container :supplier_id do
= f.label :supplier_id, t(".supplier")
%span.required *
= f.select :supplier_id, options_from_collection_for_select(@producers, :id, :name, @product.supplier_id), { include_blank: t("spree.admin.products.new.supplier_select_placeholder") }, { "data-controller": "tom-select", class: "primary" }
= render(SearchableDropdownComponent.new(form: f,
name: :supplier_id,
aria_label: t('.supplier'),
options: @producers.select(:name, :id).order(:name).pluck(:name, :id),
selected_option: @product.supplier_id,
include_blank: true,
placeholder_value: t('.search_for_suppliers')))
= f.error_message_on :supplier_id
.eight.columns.omega
= f.field_container :name do
@@ -25,8 +31,16 @@
= f.field_container :variant_unit do
= f.label :variant_unit, t(".units")
%span.required *
%select{id: 'product_variant_unit_with_scale', 'ng-model' => 'product.variant_unit_with_scale', 'ng-options' => 'unit[1] as unit[0] for unit in variant_unit_options', "data-controller": "tom-select","data-tom-select-options-value": '{"allowEmptyOption":false}', class: "primary"}
%option{'value' => '', 'ng-hide' => "hasUnit(product)"}
= f.select 'variant_unit', [],
{ include_blank: true },
{ id: 'product_variant_unit_with_scale',
name: 'product_variant_unit_with_scale',
'ng-model' => 'product.variant_unit_with_scale',
'ng-options' => 'unit[1] as unit[0] for unit in variant_unit_options',
"data-controller": "tom-select",
"data-tom-select-options-value": '{"allowEmptyOption":false}',
class: "primary",
}
%input{ type: 'hidden', 'ng-value': 'product.variant_unit', "ng-init": "product.variant_unit='#{@product.variant_unit}'", name: 'product[variant_unit]' }
%input{ type: 'hidden', 'ng-value': 'product.variant_unit_scale', "ng-init": "product.variant_unit_scale='#{@product.variant_unit_scale}'", name: 'product[variant_unit_scale]' }
= f.error_message_on :variant_unit
@@ -34,16 +48,17 @@
= f.field_container :unit_value do
= f.label :unit_value, t(".value"), 'ng-disabled' => "!hasUnit(product)"
%span.required *
%input.fullwidth{ id: 'product_unit_value', 'ng-model' => 'product.master.unit_value_with_description', :type => 'text', placeholder: "eg. 2", 'ng-disabled' => "!hasUnit(product)" }
= f.text_field :unit_value, placeholder: "eg. 2", 'ng-model' => 'product.master.unit_value_with_description', class: 'fullwidth', 'ng-disabled' => "!hasUnit(product)"
%input{ type: 'hidden', 'ng-value': 'product.master.unit_value', "ng-init": "product.master.unit_value='#{@product.unit_value}'", name: 'product[unit_value]' }
%input{ type: 'hidden', 'ng-value': 'product.master.unit_description', "ng-init": "product.master.unit_description='#{@product.unit_description}'", name: 'product[unit_description]' }
= f.error_message_on :unit_value
= render 'display_as', f: f
.six.columns.omega{ 'ng-show' => "product.variant_unit_with_scale == 'items'" }
= f.field_container :unit_name do
= f.label :product_variant_unit_name, t(".unit_name")
= f.field_container :variant_unit_name do
= f.label :variant_unit_name, t(".unit_name")
%span.required *
%input.fullwidth{ id: 'product_variant_unit_name','ng-model' => 'product.variant_unit_name', :name => 'product[variant_unit_name]', :placeholder => t('admin.products.unit_name_placeholder'), :type => 'text' }
= f.text_field :variant_unit_name, :placeholder => t('admin.products.unit_name_placeholder'), 'ng-model' => 'product.variant_unit_name', class: 'fullwidth', 'ng-init': "product.variant_unit_name='#{@product.variant_unit_name}'"
= f.error_message_on :variant_unit_name
.sixteen.columns.alpha
.eight.columns.alpha
= render 'spree/admin/products/primary_taxon_form', f: f

View File

@@ -2,5 +2,5 @@
= "#{line_item.product.name}"
- unless line_item.product.name.include? line_item.name_to_display
%span= "- #{line_item.name_to_display}"
- if line_item.options_text
= "(#{line_item.options_text})"
- if line_item.unit_to_display
= "(#{line_item.unit_to_display})"

View File

@@ -6,7 +6,7 @@
- separator = messages.values.any? ? ": " : ", "
- orders.each_with_index do |order, i|
%a{ href: order_url(order) }>= order.number
%a{ href: spree.edit_admin_order_url(order) }>= order.number
= separator if messages.values.any? || i < orders.count - 1
- if messages.values.any?
= messages[order.id] || t(".no_message_provided")

View File

@@ -6,7 +6,7 @@ export default class ColumnPreferencesController extends Controller {
connect() {
this.table = document.querySelector('table[data-column-preferences-target="table"]');
this.cols = Array.from(this.table.querySelectorAll('col'));
this.colSpanCells = this.table.querySelectorAll('th[colspan],td[colspan]');
this.colSpanCells = Array.from(this.table.querySelectorAll('th[colspan],td[colspan]'));
// Initialise data-default-col-span
this.colSpanCells.forEach((cell)=> {
cell.dataset.defaultColSpan ||= cell.colSpan;
@@ -19,6 +19,8 @@ export default class ColumnPreferencesController extends Controller {
// On checkbox changed
element.addEventListener("change", this.#showHideColumn.bind(this));
}
this.#observeProductsTableRows();
}
// private
@@ -30,14 +32,39 @@ export default class ColumnPreferencesController extends Controller {
this.table.classList.toggle(`hide-${name}`, !element.checked);
// Reset cell colspans
const hiddenColCount = this.checkboxes.filter((checkbox)=> !checkbox.checked).length;
for(const cell of this.colSpanCells) {
const span = parseInt(cell.dataset.defaultColSpan, 10) - hiddenColCount;
cell.colSpan = span;
this.#updateColSpanCell(cell);
};
}
#showHideElement(element, show) {
element.style.display = show ? "" : "none";
}
#observeProductsTableRows(){
this.productsTableObserver = new MutationObserver((mutations, _observer) => {
const mutationRecord = mutations[0];
if(mutationRecord){
const productRowElement = mutationRecord.addedNodes[0];
if(productRowElement){
const newColSpanCell = productRowElement.querySelector('td[colspan]');
newColSpanCell.dataset.defaultColSpan ||= newColSpanCell.colSpan;
this.#updateColSpanCell(newColSpanCell);
this.colSpanCells.push(newColSpanCell);
}
}
});
this.productsTableObserver.observe(this.table, { childList: true });
}
#hiddenColCount(){
return this.checkboxes.filter((checkbox)=> !checkbox.checked).length;
}
#updateColSpanCell(cell){
cell.colSpan = parseInt(cell.dataset.defaultColSpan, 10) - this.#hiddenColCount();
}
}

View File

@@ -2,9 +2,14 @@ import { Controller } from "stimulus";
export default class extends Controller {
connect() {
window.addEventListener("trix-change", this.#trixChange);
this.element.addEventListener("trix-change", this.#trixChange);
this.#trixInitialize();
window.addEventListener("trix-initialize", this.#trixInitialize);
this.element.addEventListener("trix-initialize", this.#trixInitialize);
}
disconnect() {
this.element.removeEventListener("trix-change", this.#trixChange);
this.element.removeEventListener("trix-initialize", this.#trixInitialize);
}
#trixChange = (event) => {

View File

@@ -148,7 +148,7 @@
border-bottom: 2px solid $color-tbl-bg;
&.with-image {
padding: 8px;
padding: 4px 2px;
}
}

View File

@@ -132,3 +132,5 @@
@import "app/webpacker/css/admin/trix.scss";
@import "terms_of_service_banner"; // admin_v3
@import "pages/product_preview"; // admin_v3

View File

@@ -1,11 +1,60 @@
// Navigation
//---------------------------------------------------
@mixin menu-display {
display: flex;
flex-wrap: wrap;
}
@mixin menu-link {
a {
display: inline-block;
padding: 16px 20px;
color: $color-9 !important;
text-align: center;
position: relative;
font-size: 14px;
font-weight: 600;
&:hover {
color: $red !important;
&:after {
content: "";
position: absolute;
bottom: 0;
left: 20px;
right: 20px;
height: 3px;
background: $red;
}
}
&.active {
@extend :hover;
}
}
}
.inline-menu {
margin: 0;
-webkit-margin-before: 0;
-webkit-padding-start: 0;
}
// tabs
/// use the same styling as #admin-menu via menu-display and menu-link mixins
dl.admin-tabs {
box-shadow: $box-shadow;
@include menu-display;
dd {
width: auto;
padding: 0;
@include menu-link;
}
}
nav.menu {
ul {
list-style: none;
@@ -95,33 +144,10 @@ nav.menu {
}
ul {
display: flex;
flex-wrap: wrap;
@include menu-display;
li {
a {
display: inline-block;
padding: 16px 20px;
color: $color-9 !important;
text-align: center;
position: relative;
font-size: 14px;
font-weight: 600;
&:hover {
color: $red !important;
&:after {
content: "";
position: absolute;
bottom: 0;
left: 20px;
right: 20px;
height: 3px;
background: $red;
}
}
}
@include menu-link;
&.selected a {
@extend a, :hover;

View File

@@ -40,8 +40,8 @@ $color-tbl-thead-txt: $color-headers !default;
$color-tbl-thead-bg: $light-grey !default;
$color-tbl-border: $pale-blue !default;
$padding-tbl-cell: 12px;
$padding-tbl-cell-condensed: 4px 12px;
$padding-tbl-cell-relaxed: 12px 12px;
$padding-tbl-cell-condensed: 4px 3px;
$padding-tbl-cell-relaxed: 8px 3px;
// Button colors
$color-btn-bg: $teal !default;

View File

@@ -0,0 +1,176 @@
@import "../../darkswarm/branding";
@import "../../darkswarm/mixins";
#product-preview {
// The frontend css is base on foundation-sites https://github.com/foundation/foundation-sites
// Below we copied the sections that are relevant to the product preview modal
// from foundation-sites/scss/foundations/components/_types.scss
h1,
h2,
h3,
h4,
h5,
h6 {
color: #222222;
font-family: "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif;
font-style: normal;
font-weight: normal;
line-height: 1.4;
margin-bottom: 0.5rem;
margin-top: 0.2rem;
text-rendering: optimizeLegibility;
}
h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 1.4;
}
h3 {
font-size: 1.6875rem;
}
em,
i {
font-style: italic;
line-height: inherit;
}
ul,
ol,
dl {
font-family: inherit;
font-size: 1rem;
line-height: 1.6;
list-style-position: outside;
margin-bottom: 1.25rem;
}
.text-right {
text-align: right !important;
}
// from foundation-sites/scss/foundations/components/_buttons.scss
button,
.button {
-webkit-appearance: none;
-moz-appearance: none;
border-radius: 0;
border-style: solid;
border-width: 0;
cursor: pointer;
font-family: "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif;
font-weight: normal;
line-height: normal;
margin: 0 0 1.25rem;
position: relative;
text-align: center;
text-decoration: none;
display: inline-block;
padding: 1rem 2rem 1.0625rem 2rem;
font-size: 1rem;
background-color: #008cba;
border-color: #007095;
color: #ffffff;
transition: background-color 300ms ease-out;
}
// from foundation-sites/scss/foundations/components/_grid.scss
@media only screen and (min-width: 64.0625em) {
.column,
.columns {
position: relative;
padding-left: 0.9375rem;
padding-right: 0.9375rem;
float: left;
}
}
.column + .column:last-child,
.column + .columns:last-child,
.columns + .column:last-child,
.columns + .columns:last-child {
float: right;
}
.small-3 {
width: 25%;
}
@media only screen and (min-width: 40.0625em) {
.medium-3 {
width: 20%; // original value 25%
}
}
@media only screen and (min-width: 64.0625em) {
.large-3 {
width: 25%;
}
}
@media only screen and (min-width: 64.0625em) {
.large-6 {
width: 50%;
}
}
// from foundation-sites/scss/foundations/components/_global.scss
img {
display: inline-block;
vertical-align: middle;
}
// Import frontend partials
// Product details
@import "../../darkswarm/shop_partials/typography";
@import "../../darkswarm/overrides";
.row {
margin: 0 auto;
width: 100%;
}
@import "../../darkswarm/shop_partials/animations";
@import "../../darkswarm/shop_partials/shop-filters";
@import "../../darkswarm/shop-modals";
@import "../../darkswarm/shop_partials/images";
// Shop
@import "../../darkswarm/shop_partials/shop-product-thumb";
@import "../../darkswarm/shop_partials/shop-product-rows";
@import "../../darkswarm/shop_partials/shop-inputs";
@import "../../shared/question-mark-icon";
button.add-variant {
opacity: 0.5;
}
.variant-remaining-stock {
opacity: 0.5;
}
.question-mark-icon {
display: block;
}
.tooltip {
@include joyride-content;
width: $joyride-width;
text-transform: none;
}
.arrow {
background-color: $dynamic-blue;
}
.columns {
margin-left: 0;
}
}

View File

@@ -1,102 +1,4 @@
@mixin filter-selector($base-clr, $border-clr, $hover-clr) {
&.inline-block, ul.inline-block {
display: inline-block;
}
li {
display: inline-block;
@include border-radius(0);
padding: 0;
margin: 0 0.5rem 0.5rem 0;
&:hover, &:focus {
background: transparent;
}
&.active {
box-shadow: none;
}
a, a.button {
display: block;
@include border-radius(0.5em);
border: 1px solid $border-clr;
padding: 0.5em 0.625em;
color: $base-clr;
font-size: 0.75em;
background: white;
margin: 0;
i {
padding-left: 0.25rem;
}
render-svg {
&, & svg {
width: 1rem;
height: 1rem;
float: left;
padding-right: 0.25rem;
path {
@include csstrans;
fill: $base-clr;
}
}
}
&:hover, &:focus {
border-color: $hover-clr;
color: $hover-clr;
render-svg {
svg {
path {
fill: $hover-clr;
}
}
}
}
&.disabled {
opacity: 0.6;
&:hover, &:focus {
border-color: $border-clr;
color: $base-clr;
render-svg {
svg {
path {
fill: $base-clr;
}
}
}
}
}
&.active, &.active:hover, &.active:focus {
border: 1px solid $base-clr;
background: $base-clr;
color: white;
render-svg {
svg {
path {
fill: white;
}
}
}
}
}
}
}
@import "shop_partials/shop-filters";
// Alert when search, taxon, filter is triggered
@@ -167,23 +69,3 @@
max-height: calc(100vh - #{$topbar-height});
overflow-y: auto;
}
.filter-shopfront {
&.taxon-selectors, &.property-selectors {
background: transparent;
}
// Shopfront taxons
&.taxon-selectors {
@include filter-selector($clr-blue, $clr-blue-light, $clr-blue-bright);
}
// Shopfront properties
&.property-selectors {
@include filter-selector(#666, #ccc, #777);
}
ul {
margin: 0;
}
}

View File

@@ -15,81 +15,7 @@
//
// They are not nested so that they can be used in modals.
.variant-quantity-inputs {
height: 2.5rem;
white-space: nowrap;
}
button.add-variant, button.variant-quantity {
height: 2.5rem;
border-radius: 0;
background-color: $orange-500;
color: white;
// Override foundation button styles:
font-size: 1rem;
margin: 0;
padding: 0;
transition: none;
&:hover {
background-color: $orange-600;
}
&[disabled] {
background-color: $grey-400;
&:hover, &:focus {
background-color: $grey-400;
}
}
&:nth-of-type(1) {
border-bottom-left-radius: 0.25em;
border-top-left-radius: 0.25em;
}
&:nth-last-of-type(1) {
border-top-right-radius: 0.25em;
border-bottom-right-radius: 0.25em;
}
}
button.add-variant {
min-width: 7rem;
padding: 0 1em;
&[disabled] {
&:hover, &:focus {
background-color: $orange-500;
}
}
}
button.variant-quantity {
width: 2.25rem;
&:nth-of-type(1):not(.bulk-buy):not(.bulk-buy-add) {
border-right: .1em solid $orange-400;
}
}
.variant-quantity-display, .variant-remaining-stock {
font-size: 0.875em;
margin-top: 0.25em;
text-align: center;
width: 7rem;
display: inline-block;
}
.variant-quantity-display {
visibility: hidden;
&.visible {
visibility: visible;
}
}
.variant-remaining-stock {
color: $red-500;
}
@import "shop_partials/shop-inputs";
button.bulk-buy.variant-quantity {
background-color: transparent;

View File

@@ -1,201 +1,7 @@
.darkswarm {
products {
product {
// GENERAL LAYOUT
.row {
.columns {
padding-top: 0em;
padding-bottom: 0em;
line-height: 1.1;
}
}
.shop-variants {
// product-thumb width + 1rem
padding-left: calc(22.222% + 1rem);
@include breakpoint(phablet) {
padding-left: 0;
clear: left;
}
}
// ROW VARIANTS
.row.variants {
margin: 0 0 1em 0;
&.out-of-stock {
opacity: 0.2;
}
.variant-name,
.total-price {
padding-top: .74em;
}
.variant-price {
padding-top: .65em;
}
// Variant name
.variant-name {
padding-left: 0;
padding-right: 0;
@include breakpoint(phablet) {
padding-left: 0.5rem;
}
& > *:nth-child(n + 2) {
color: $grey-550;
font-size: 0.875rem;
font-style: italic;
line-height: normal;
}
}
// Variant price
.variant-price {
white-space: nowrap;
@include breakpoint(phablet) {
padding-left: 1rem;
}
}
.variant-unit-price {
color: $grey-700;
font-size: 0.85rem;
margin-top: 15px;
position: relative;
left: -1px;
}
// Total price
.total-price {
padding-left: 0rem;
color: $disabled-med;
.filled {
color: $med-drk-grey;
}
@include breakpoint(phablet) {
display: none;
}
}
}
// ROW SUMMARY
.summary {
margin-left: 0;
margin-right: 0;
margin-bottom: 1.25em;
background: #fff;
.columns {
padding-top: 1em;
padding-bottom: 1em;
line-height: 1;
@include breakpoint(tablet) {
padding-top: 0.65rem;
padding-bottom: 0.65rem;
}
}
.summary-header {
// product-thumb width + 1rem
padding-left: calc(22.222% + 1rem);
padding-right: 1rem;
@include breakpoint(phablet) {
padding-left: calc(33.333% + 1rem);
}
.product-producer {
color: $grey-550;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-style: italic;
a {
color: $teal-500;
&:hover, &:focus, &:active {
color: $teal-600;
text-decoration: underline;
}
}
}
h3 {
font-size: 1.3rem;
margin-top: 0.75rem;
margin-bottom: 0.6rem;
}
h3 a {
color: $orange-500;
&:hover, &:focus, &:active {
color: $orange-600;
text-decoration: underline;
}
}
.product-description {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 0.75rem;
cursor: pointer;
// Force product description to be on one line
// and truncate with ellipsis
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
// line-clamp is not supported in Safari
// Trick to get overflow: hidden to work in old Safari
line-height: 1rem;
height: 1.75rem;
> div {
margin-bottom: 1.5rem; // Equivalent to p (trix doesn't use p as separator by default, so emulate div as p to be backward compatible)
}
@include trix-styles;
}
.product-properties {
margin: .5em 0;
li {
margin: 0 0.25rem 0.25rem 0;
a {
padding: 0.1em 0.625em;
cursor: auto;
&.has-tip {
cursor: pointer;
font-weight: normal;
}
&:hover, &:focus {
border-color: #ccc;
}
}
// Foundation doesn't show the nub on mobile.
// Repeating the style to show it here.
.nub {
border-color: transparent transparent #333333 transparent;
}
}
}
}
}
@import "shop_partials/shop-product-rows";
}
}
}

View File

@@ -1,34 +1,7 @@
.darkswarm {
products {
product {
.product-thumb {
// Desktop: the product summary is nine columns wide. Use two
// for the image. 100% / 9 * 2 = 22.222% <= 192px
width: calc(22.222%);
float: left;
// Mobile: the summary has full twelve columns and the image
// should take four of them. 100% / 12 * 4 = 33.333% <= 227px
@include breakpoint(phablet) {
width: calc(33.333%);
}
// Make this an anchor for the bulk label.
position: relative;
.product-thumb__bulk-label {
background-color: $grey-700;
color: white;
position: absolute;
right: 0;
top: .8em;
padding: .25em .5em;
}
&:hover {
filter: brightness(96%);
}
}
@import "shop_partials/shop-product-thumb";
}
}
}

View File

@@ -275,11 +275,4 @@ product.animate-repeat {
}
}
@mixin csstrans {
-webkit-transition: all 300ms ease;
-moz-transition: all 300ms ease;
-ms-transition: all 300ms ease;
-o-transition: all 300ms ease;
transition: all 300ms ease;
-webkit-transform-style: preserve-3d;
}
@import "shop_partials/animations";

View File

@@ -1,23 +1,4 @@
.product-img {
text-align: center;
img {
padding: 0.3rem;
// placeholder for when no product images
&.placeholder {
opacity: 0.35;
@include breakpoint(desktop) {
display: none;
}
}
@media only screen and (max-width: 1024px) {
margin: 0 0 0.5rem;
}
}
}
@import "shop_partials/images";
.hero-img {
outline: 1px solid $disabled-bright;

View File

@@ -0,0 +1,8 @@
@mixin csstrans {
-webkit-transition: all 300ms ease;
-moz-transition: all 300ms ease;
-ms-transition: all 300ms ease;
-o-transition: all 300ms ease;
transition: all 300ms ease;
-webkit-transform-style: preserve-3d;
}

View File

@@ -0,0 +1,21 @@
.product-img {
text-align: center;
img {
padding: 0.3rem;
// placeholder for when no product images
&.placeholder {
opacity: 0.35;
@include breakpoint(desktop) {
display: none;
}
}
@media only screen and (max-width: 1024px) {
margin: 0 0 0.5rem;
}
}
}

View File

@@ -0,0 +1,119 @@
@mixin filter-selector($base-clr, $border-clr, $hover-clr) {
&.inline-block, ul.inline-block {
display: inline-block;
}
li {
display: inline-block;
@include border-radius(0);
padding: 0;
margin: 0 0.5rem 0.5rem 0;
&:hover, &:focus {
background: transparent;
}
&.active {
box-shadow: none;
}
a, a.button {
display: block;
@include border-radius(0.5em);
border: 1px solid $border-clr;
padding: 0.5em 0.625em;
color: $base-clr;
font-size: 0.75em;
background: white;
margin: 0;
i {
padding-left: 0.25rem;
}
render-svg {
&, & svg {
width: 1rem;
height: 1rem;
float: left;
padding-right: 0.25rem;
path {
@include csstrans;
fill: $base-clr;
}
}
}
&:hover, &:focus {
border-color: $hover-clr;
color: $hover-clr;
render-svg {
svg {
path {
fill: $hover-clr;
}
}
}
}
&.disabled {
opacity: 0.6;
&:hover, &:focus {
border-color: $border-clr;
color: $base-clr;
render-svg {
svg {
path {
fill: $base-clr;
}
}
}
}
}
&.active, &.active:hover, &.active:focus {
border: 1px solid $base-clr;
background: $base-clr;
color: white;
render-svg {
svg {
path {
fill: white;
}
}
}
}
}
}
}
.filter-shopfront {
&.taxon-selectors, &.property-selectors {
background: transparent;
}
// Shopfront taxons
&.taxon-selectors {
@include filter-selector($clr-blue, $clr-blue-light, $clr-blue-bright);
}
// Shopfront properties
&.property-selectors {
@include filter-selector(#666, #ccc, #777);
}
ul {
margin: 0;
}
}

View File

@@ -0,0 +1,76 @@
.variant-quantity-inputs {
height: 2.5rem;
white-space: nowrap;
}
button.add-variant, button.variant-quantity {
height: 2.5rem;
border-radius: 0;
background-color: $orange-500;
color: white;
// Override foundation button styles:
font-size: 1rem;
margin: 0;
padding: 0;
transition: none;
&:hover {
background-color: $orange-600;
}
&[disabled] {
background-color: $grey-400;
&:hover, &:focus {
background-color: $grey-400;
}
}
&:nth-of-type(1) {
border-bottom-left-radius: 0.25em;
border-top-left-radius: 0.25em;
}
&:nth-last-of-type(1) {
border-top-right-radius: 0.25em;
border-bottom-right-radius: 0.25em;
}
}
button.add-variant {
min-width: 7rem;
padding: 0 1em;
&[disabled] {
&:hover, &:focus {
background-color: $orange-500;
}
}
}
button.variant-quantity {
width: 2.25rem;
&:nth-of-type(1):not(.bulk-buy):not(.bulk-buy-add) {
border-right: .1em solid $orange-400;
}
}
.variant-quantity-display, .variant-remaining-stock {
font-size: 0.875em;
margin-top: 0.25em;
text-align: center;
width: 7rem;
display: inline-block;
}
.variant-quantity-display {
visibility: hidden;
&.visible {
visibility: visible;
}
}
.variant-remaining-stock {
color: $red-500;
}

View File

@@ -0,0 +1,195 @@
// GENERAL LAYOUT
.row {
.columns {
padding-top: 0em;
padding-bottom: 0em;
line-height: 1.1;
}
}
.shop-variants {
// product-thumb width + 1rem
padding-left: calc(22.222% + 1rem);
@include breakpoint(phablet) {
padding-left: 0;
clear: left;
}
}
// ROW VARIANTS
.row.variants {
margin: 0 0 1em 0;
&.out-of-stock {
opacity: 0.2;
}
.variant-name,
.total-price {
padding-top: .74em;
}
.variant-price {
padding-top: .65em;
}
// Variant name
.variant-name {
padding-left: 0;
padding-right: 0;
@include breakpoint(phablet) {
padding-left: 0.5rem;
}
& > *:nth-child(n + 2) {
color: $grey-550;
font-size: 0.875rem;
font-style: italic;
line-height: normal;
}
}
// Variant price
.variant-price {
white-space: nowrap;
@include breakpoint(phablet) {
padding-left: 1rem;
}
}
.variant-unit-price {
color: $grey-700;
font-size: 0.85rem;
margin-top: 15px;
position: relative;
left: -1px;
}
// Total price
.total-price {
padding-left: 0rem;
color: $disabled-med;
.filled {
color: $med-drk-grey;
}
@include breakpoint(phablet) {
display: none;
}
}
}
// ROW SUMMARY
.summary {
margin-left: 0;
margin-right: 0;
margin-bottom: 1.25em;
background: #fff;
.columns {
padding-top: 1em;
padding-bottom: 1em;
line-height: 1;
@include breakpoint(tablet) {
padding-top: 0.65rem;
padding-bottom: 0.65rem;
}
}
.summary-header {
// product-thumb width + 1rem
padding-left: calc(22.222% + 1rem);
padding-right: 1rem;
@include breakpoint(phablet) {
padding-left: calc(33.333% + 1rem);
}
.product-producer {
color: $grey-550;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-style: italic;
a {
color: $teal-500;
&:hover, &:focus, &:active {
color: $teal-600;
text-decoration: underline;
}
}
}
h3 {
font-size: 1.3rem;
margin-top: 0.75rem;
margin-bottom: 0.6rem;
}
h3 a {
color: $orange-500;
&:hover, &:focus, &:active {
color: $orange-600;
text-decoration: underline;
}
}
.product-description {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 0.75rem;
cursor: pointer;
// Force product description to be on one line
// and truncate with ellipsis
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
// line-clamp is not supported in Safari
// Trick to get overflow: hidden to work in old Safari
line-height: 1rem;
height: 1.75rem;
> div {
margin-bottom: 1.5rem; // Equivalent to p (trix doesn't use p as separator by default, so emulate div as p to be backward compatible)
}
@include trix-styles;
}
.product-properties {
margin: .5em 0;
li {
margin: 0 0.25rem 0.25rem 0;
a {
padding: 0.1em 0.625em;
cursor: auto;
&.has-tip {
cursor: pointer;
font-weight: normal;
}
&:hover, &:focus {
border-color: #ccc;
}
}
// Foundation doesn't show the nub on mobile.
// Repeating the style to show it here.
.nub {
border-color: transparent transparent #333333 transparent;
}
}
}
}
}

View File

@@ -0,0 +1,28 @@
.product-thumb {
// Desktop: the product summary is nine columns wide. Use two
// for the image. 100% / 9 * 2 = 22.222% <= 192px
width: calc(22.222%);
float: left;
// Mobile: the summary has full twelve columns and the image
// should take four of them. 100% / 12 * 4 = 33.333% <= 227px
@include breakpoint(phablet) {
width: calc(33.333%);
}
// Make this an anchor for the bulk label.
position: relative;
.product-thumb__bulk-label {
background-color: $grey-700;
color: white;
position: absolute;
right: 0;
top: .8em;
padding: .25em .5em;
}
&:hover {
filter: brightness(96%);
}
}

View File

@@ -0,0 +1,31 @@
@mixin headingFont {
font-family: "Oswald", sans-serif;
}
// TODO should probably move that to a variables.scss
$body-font: "Roboto", Arial, sans-serif;
h1, h2, h3, h4, h5, h6 {
@include headingFont;
padding: 0px;
}
a {
color: $clr-brick;
&:hover, &:focus, &:active {
text-decoration: none;
color: $clr-brick-bright;
}
}
.text-small {
font-size: 0.875rem;
margin-bottom: 0.5rem;
font-family: $body-font;
&, & * {
font-size: 0.875rem;
}
}

View File

@@ -347,7 +347,6 @@
margin-top: 40px;
.button.primary {
background-color: $clr-turquoise;
&:hover {
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
}

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