Skip to content

Update/migrate webpacker to js bundeling - #1108

Merged
lodewiges merged 58 commits into
stagingfrom
update/migrateWebpackerToJSBundeling
Nov 21, 2025
Merged

Update/migrate webpacker to js bundeling#1108
lodewiges merged 58 commits into
stagingfrom
update/migrateWebpackerToJSBundeling

Conversation

@lodewiges

@lodewiges lodewiges commented Oct 28, 2025

Copy link
Copy Markdown
Contributor

Remove the old way of serving javascript through rails and replaced it with a rails 7 alternative.
This was needed because it was base on webpack 4 and we needed to move to webpack 5. Webpack 4 is already 3 years old.

The goal was to get all assest handeld by js-bundeling rails this is the reason why we also switched the way of serving icons

Besides that because i was overhauling the javascript pipeline. I could do some needed migrations

  • Migrated Eslint-loader to Eslint-Webpack-Plugin
  • Migrated Postcss-cssnext to Postcss-preset-env
  • Migrated Sass-lint to stylelint (sameone as amber for css)

Fixes #976
Fixes #977
Fixes #978
Fixes #979
Fixes #688

5 errors

TO DO

  • get icons working
  • fix ActionController::InvalidAuthenticityToken (Can't verify CSRF token authenticity.):
  • [Vue warn]: Error in created hook: "TypeError: Cannot read properties of undefined (reading 'get')"
  • get js working
  • migrate all templates to axios
  • stop using import if possible
  • linting errors
  • get order screen working
  • get login working
  • Plus and minus icon on the edit orderscreen
  • check loose mode warnings webpack
  • error when trying to edit order

Summary by CodeRabbit

  • New Features

    • Enhanced search functionality with improved responsiveness and loading indicators for better user experience.
  • Bug Fixes

    • Improved error handling and validation in order management workflows.
    • Fixed parameter handling in order updates for more reliable data processing.
  • Style

    • Updated visual icons and styling throughout the application for a modern, consistent design.
    • Improved layout and spacing in order screens.
  • Chores

    • Modernized asset pipeline and development tooling for improved performance and maintainability.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Oct 28, 2025

Copy link
Copy Markdown

Warning

Rate limit exceeded

@lodewiges has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 5 minutes and 56 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between d12cb43 and 1f3f528.

📒 Files selected for processing (1)
  • app/javascript/order_screen.js (8 hunks)

Walkthrough

This PR comprehensively migrates the application from Turbolinks to Turbo, replaces Webpacker with native Rails 7+ asset bundling (jsbundling, cssbundling), updates the JavaScript build chain (webpack, Babel), modernizes linting tools (ESLint flat config, Stylelint), and updates FontAwesome icon usage across views and components.

Changes

Cohort / File(s) Summary
Asset Pipeline & Webpack Configuration
.babelrc, babel.config.js, webpack.config.js, config/webpack/*, config/webpacker.yml, app/assets/config/manifest.js, config/initializers/assets.rb
Removed Webpacker configuration and related webpack loaders. Added native webpack.config.js with Vue, Babel, CSS, and ESLint plugin support. Updated Babel config to use useBuiltIns: "usage", corejs 3, and babel-plugin-macros. Replaced webpacker paths with single app/assets/builds directory.
Build & Package Management
package.json, Gemfile, .browserslistrc, Dockerfile
Added jsbundling-rails, cssbundling-rails, dartsass-rails; removed webpacker, turbolinks, sass-lint. Updated dependencies: Turbo Rails, Babel runtime, FontAwesome, core-js, webpack. Added npm scripts (lint, build, watch, build:css). Updated Node.js setup to v18 in Docker.
JavaScript Event Listener Migration
app/javascript/application.js, app/javascript/activities.js, app/javascript/activity.js, app/javascript/credit_mutations.js, app/javascript/invoices.js, app/javascript/order_screen.js, app/javascript/payment_add.js, app/javascript/price_lists.js, app/javascript/user.js, app/javascript/users.js
Migrated event listeners from turbolinks:load to turbo:load and turbolinks:before-cache to turbo:before-cache. Removed TurbolinksAdapter usage. Updated component import paths (relative syntax). Preserved Vue initialization logic with Turbo lifecycle guards.
Removed Pack Entrypoints
app/javascript/packs/activity.js, app/javascript/packs/invoices.js, app/javascript/packs/users.js, app/assets/javascripts/application.js
Deleted legacy Sprockets manifest and Webpacker pack files. Consolidated Vue initialization into top-level JavaScript modules.
Linting Configuration
.eslintrc.json, eslint.config.mjs, .stylelintrc.json, .sass-lint.yml, .postcssrc.yml, bin/ci.sh
Removed old ESLint JSON config and Sass Lint. Added ESLint flat config (eslint.config.mjs) with Vue parser support. Added Stylelint config extending standard-scss and recess-order. Updated CI script to use new yarn lint:styles. Removed PostCSS plugins block.
View Layout & Asset Tags
app/views/layouts/application.html.erb, app/views/layouts/errors.html.erb
Updated stylesheet and script tags from Turbolinks to Turbo attributes. Added viewport meta tag, CSRF/CSP meta tags, and yield(:head) placeholder. Added defer attribute to JavaScript includes. Simplified asset tag signatures.
Individual View Asset Inclusion
app/views/activities/*, app/views/credit_mutations/index.html.erb, app/views/invoices/index.html.erb, app/views/payments/add.html.erb, app/views/price_lists/index.html.erb, app/views/users/*
Added content_for :head blocks in each view to load corresponding JavaScript assets (activities.js, credit_mutations.js, invoices.js, etc.) with turbo-track reload and defer attributes.
FontAwesome Icon Replacements
app/views/activities/*, app/views/credit_mutations/index.html.erb, app/views/invoices/_modal.html.erb, app/views/price_lists/index.html.erb, app/views/users/*, app/views/index/index.html.erb, app/views/partials/_*
Replaced Rails FontAwesome helper (fa_icon) calls with direct Font Awesome 5+ <i> elements (e.g., <i class="fas fa-plus"></i>). Updated icon class names where FontAwesome 5+ uses different names.
Turbo Method Syntax
app/views/partials/_navigation_bar.html.erb, app/views/partials/_login_prompt.html.erb
Updated Rails link helpers from method: :post/method: :delete to data: { turbo_method: :post }/data: { turbo_method: :delete }.
Stylesheets
app/assets/stylesheets/application.scss, app/assets/stylesheets/order_screen.scss, app/assets/stylesheets/theme_sofia.scss
Updated Bootstrap import from generic to @import 'bootstrap/scss/bootstrap'. Removed FontAwesome and Sass-Lint imports. Refactored order_screen layout to grid-based system with grid-area assignments. Minor formatting and whitespace normalization in theme_sofia.
Vue Components
app/javascript/components/activity/ProductTotals.vue, app/javascript/components/ProductTable.vue, app/javascript/components/UserInput.vue, app/javascript/components/orderscreen/*, app/javascript/components/user/OrderHistory.vue
Updated FontAwesome icon classes to v5+ (e.g., fa-plus-square-o → far fa-square-plus). Refactored UserInput with 400ms debounce and improved suggestion handling. Added error logging and state management improvements in ProductTable and OrderHistory. Template literal string formatting in ProductTotals.
Controller & Parameters
app/controllers/orders_controller.rb, spec/controllers/orders_controller/update_spec.rb
Changed strong parameters to nest order_rows_attributes under order key. Updated order lookup to use params[:id] directly. Modified test expectations to match new parameter structure.
Development Tooling
Procfile.dev, bin/dev, bin/webpack, bin/webpack-dev-server, config/environments/*
Added Procfile.dev and bin/dev for multi-process development (web, js, css). Removed webpack and webpack-dev-server bin scripts. Removed webpacker-related config from development/production environments. Fixed caching-dev.txt path.
Generator & Documentation
lib/generators/rails/webpacker_assets/webpacker_assets_generator.rb, lib/generators/rails/webpacker_assets/USAGE
Removed Webpacker asset generator and usage documentation.
Configuration Files
.gitignore, .slim-lint.yml, config/puma.rb, README.md
Updated .gitignore to ignore app/assets/builds. Removed LineLength rule from .slim-lint config. Updated README references from Turbolinks to Turbo. Minor port comment clarification in puma.rb.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant Browser
    participant OldApp as App (Turbolinks)
    participant NewApp as App (Turbo)
    
    rect rgb(200, 220, 255)
    note over OldApp: Old Flow
    User->>Browser: Click link
    Browser->>OldApp: turbolinks:load
    OldApp->>OldApp: Initialize Vue + TurbolinksAdapter
    OldApp-->>Browser: Render page
    User->>Browser: Navigate away
    Browser->>OldApp: turbolinks:before-cache
    OldApp->>OldApp: Cleanup (if implemented)
    end
    
    rect rgb(200, 255, 220)
    note over NewApp: New Flow
    User->>Browser: Click link
    Browser->>NewApp: turbo:load
    NewApp->>NewApp: Initialize Vue (no adapter)
    NewApp-->>Browser: Render page
    User->>Browser: Navigate away
    Browser->>NewApp: turbo:before-cache
    NewApp->>NewApp: Destroy Vue instance
    end
Loading
sequenceDiagram
    autonumber
    participant Build as Build System
    participant Webpack as Webpack
    participant Assets as app/assets/builds
    
    rect rgb(220, 240, 255)
    note over Build: Old Webpacker Pipeline
    Build->>Webpack: webpacker gem
    Webpack->>Webpack: Process packs/<entry>.js
    Webpack-->>Assets: public/packs/<entry>.js
    end
    
    rect rgb(255, 240, 220)
    note over Build: New Rails 7+ Pipeline
    Build->>Webpack: native webpack.config.js
    Webpack->>Webpack: Process app/javascript/*.js entries
    Webpack-->>Assets: app/assets/builds/<entry>.js
    Assets->>Assets: Sprockets compiles & serves
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • webpack.config.js: New file with dynamic entry resolution, ESLintPlugin config, and Vue/Babel loader setup; verify entry detection and plugin configuration
  • package.json: Extensive dependency changes (removals, additions, upgrades); validate version compatibility and peer dependency constraints
  • app/javascript/ entrypoints: Verify Turbo event listener migration is complete and Vue lifecycle hooks properly destroy instances to prevent memory leaks
  • app/assets/stylesheets/order_screen.scss: Major grid-based layout refactoring; test layout behavior across viewport sizes
  • app/javascript/components/UserInput.vue: New debounce logic and refactored suggestion handling; verify dropdown behavior and edge cases with rapid input
  • .babelrc vs babel.config.js coexistence: Confirm only one is used; verify useBuiltIns "usage" doesn't break polyfill expectations
  • Strong parameters in orders_controller.rb: New nesting structure; ensure all client-side payload construction aligns with new parameter expectations
  • FontAwesome 5+ icon class updates: Verify all icon classes render correctly (e.g., far vs fas vs fal variants)

Possibly related PRs

  • csvalpha/sofia#1106: Related to webpacker_assets generator modifications/relocation alongside this PR's deletion of the generator.

Poem

🐰 Turbo hops in, Webpacker bows out,
Webpack spins fresh, no need to doubt—
From Turbolinks old to Rails 7 new,
Assets pipeline blooms in lighter hue!
FontAwesome icons shine so bright,
Dev setup dances through the night.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description covers key objectives and completed work, but does not follow the template's structured sections. The Checklist section is absent, and the Summary/Other information structure is informal. Restructure the description to include the formal Checklist section and clearly separate Summary, Related Issues, and Other Information sections as specified in the template.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective: migrating from Webpacker to Rails 7's js-bundling. It is concise and clearly conveys the primary change.
Linked Issues check ✅ Passed All five linked issues are addressed: Turbolinks→Turbo migration (#976), Webpacker→js-bundling (#977), PostCSS preset update (#978), ESLint tooling migration (#979), and sass-lint replacement (#688). Code changes demonstrate comprehensive implementation of all requirements.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the migration objectives. Babel config, webpack setup, package.json updates, component refactoring, icon replacements, and template updates are all necessary for completing the Webpacker→js-bundling migration and related tooling updates.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as outdated.

@codecov

codecov Bot commented Oct 28, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.27%. Comparing base (6403bb3) to head (1f3f528).
⚠️ Report is 3 commits behind head on staging.

Additional details and impacted files
@@             Coverage Diff             @@
##           staging    #1108      +/-   ##
===========================================
+ Coverage    74.10%   75.27%   +1.17%     
===========================================
  Files           51       50       -1     
  Lines         1093     1076      -17     
===========================================
  Hits           810      810              
+ Misses         283      266      -17     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/javascript/price_lists.js (1)

116-125: Replace Vue.util.extend with Object.assign or spread operator.

Vue.util is an internal API not intended for public use and may change in future Vue versions. Use Object.assign or the spread operator for shallow cloning instead.

Apply this diff:

 editProduct: function(product) {
   // Save original state
-  product._beforeEditingCache = Vue.util.extend({}, product);
+  product._beforeEditingCache = Object.assign({}, product);
   product.product_prices.forEach((pp, i) => {
-    product._beforeEditingCache.product_prices[i] = Vue.util.extend({}, pp);
+    product._beforeEditingCache.product_prices[i] = Object.assign({}, pp);
   });
   
   product.editing = true;
   return product;
 },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 097bc3d and e8af0a0.

📒 Files selected for processing (1)
  • app/javascript/price_lists.js (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/javascript/price_lists.js (8)
app/javascript/order_screen.js (1)
  • element (9-9)
app/javascript/invoices.js (1)
  • element (16-16)
app/javascript/credit_mutations.js (1)
  • element (16-16)
app/javascript/payment_add.js (1)
  • element (6-6)
app/javascript/users.js (1)
  • element (14-14)
app/javascript/user.js (1)
  • element (7-7)
app/javascript/activity.js (1)
  • element (15-15)
app/javascript/activities.js (2)
  • element (4-4)
  • priceLists (6-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build

if (sanitizedProduct.id) { // Existing product
this.$http.put(`/products/${sanitizedProduct.id}.json`, { product: sanitizedProduct }).then( (response) => {
var newProduct = response.data;
this.$http.put(`/products/${sanitizedProduct.id}.json`, { product: sanitizedProduct }).then((response) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

this.$http is undefined — vue-resource is not imported or configured.

The code uses this.$http.put (line 64), this.$http.post (lines 73, 139, 147), but vue-resource is neither imported nor configured in this file. This will cause runtime errors when these methods are called. Past review comments indicate axios was intended (commit 548e00f), but the HTTP calls were never migrated from this.$http to axios.

Install vue-resource and configure it globally, or migrate to axios. Given the modernization goals of this PR, migrating to axios is recommended:

Step 1: Import axios at the top of the file:

 import Vue from 'vue/dist/vue.esm';
+import axios from 'axios';

Step 2: Replace this.$http calls with axios. For example, line 64:

-this.$http.put(`/products/${sanitizedProduct.id}.json`, { product: sanitizedProduct }).then((response) => {
-  const newProduct = response.body;
+axios.put(`/products/${sanitizedProduct.id}.json`, { product: sanitizedProduct }).then((response) => {
+  const newProduct = response.data;
   newProduct.editing = false;
   
   this.$set(this.products, this.products.indexOf(product), newProduct);
 }).catch((error) => {
-  this.errors = error.response?.body?.errors || ['An error occurred'];
+  this.errors = error.response?.data?.errors || ['An error occurred'];
 });

Apply the same pattern to lines 73, 139, and 147 (replace response.body with response.data and error.response?.body with error.response?.data).

Also applies to: 73-73, 139-139, 147-147

🤖 Prompt for AI Agents
In app/javascript/price_lists.js around lines 64, 73, 139 and 147, the code uses
this.$http which is undefined because vue-resource isn't imported/configured;
migrate these calls to axios by importing axios at the top of the file and
replacing this.$http.put/post(...) with axios.put/post(...), update success
handling to use response.data instead of response.body, and update error
handling to use error.response?.data instead of error.response?.body; ensure the
promise chaining/async handling remains the same and remove any dependency on
this.$http throughout the file.

coderabbitai[bot]

This comment was marked as outdated.

@lodewiges
lodewiges force-pushed the update/migrateWebpackerToJSBundeling branch from 06cfb6a to f276172 Compare November 17, 2025 15:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (2)
app/javascript/price_lists.js (2)

143-148: Fix inconsistent response property access in archivePriceList.

The method uses VueResource's this.$http (line 143) but accesses response.data (line 144) and error.response?.data?.errors (line 146), which are axios-style properties. VueResource uses response.body.

Apply this diff:

 this.$http.post(`/price_lists/${priceList.id}/archive`, {}).then((response) => {
-  priceList.archived_at = response.data;
+  priceList.archived_at = response.body;
 }).catch((error) => {
-  this.errors = error.response?.data?.errors || ['An error occurred'];
+  this.errors = error.body?.errors || ['An error occurred'];
 });

150-156: Fix inconsistent response property access in unarchivePriceList.

Same issue as archivePriceList: the method uses VueResource but accesses axios-style response properties.

Apply this diff:

 this.$http.post(`/price_lists/${priceList.id}/unarchive`, {}).then((response) => {
-  priceList.archived_at = response.data;
+  priceList.archived_at = response.body;
 }).catch((error) => {
-  this.errors = error.response?.data?.errors || ['An error occurred'];
+  this.errors = error.body?.errors || ['An error occurred'];
 });
🧹 Nitpick comments (3)
app/javascript/components/activity/ProductTotals.vue (1)

84-87: Consider adding user-facing error feedback.

The error handler correctly stops the spinner and logs the error, but users receive no indication that the request failed. When an error occurs, the component displays the empty-state message "Er zijn nog geen producten verkocht," which misleadingly suggests there's no data rather than a failure to load. Consider adding an error state to inform users of the issue.

For example, add an error data property and display it in the template:

  data() {
    return {
      user: {},
      orderTotals: [],
      totalAmount: 0.0,
-     isLoading: true
+     isLoading: true,
+     error: null
    };
  },
  loadProductTotals() {
    this.isLoading = true;
+   this.error = null;

    let params = {user: this.user.id, paid_with_cash: this.user.paid_with_cash, paid_with_pin: this.user.paid_with_pin};
    this.$http.get(`/activities/${this.activity}/product_totals`, { params }).then((response) => {
      this.orderTotals = response.body;
      this.totalAmount = this.orderTotals.reduce((a, b) => a + parseFloat(b.price), 0.0);
      this.isLoading = false;
    }).catch((error) => {
      this.isLoading = false;
+     this.error = 'Er is een fout opgetreden bij het laden van de producttotalen.';
      console.error(error);
    });
  },

And update the template to show the error when present:

<div class="alert alert-danger" v-if="error">
  {{ error }}
</div>
app/javascript/application.js (1)

22-22: Remove empty event handler or document its purpose.

The empty turbo:load handler serves no function. If this is a placeholder for future code, add a comment explaining that; otherwise, remove it.

Apply this diff if no future use is planned:

-document.addEventListener('turbo:load', () => {});
app/javascript/price_lists.js (1)

1-164: Consider completing the migration from VueResource to axios.

Past review comments suggested migrating from VueResource (this.$http) to axios for modernization. The current code still uses VueResource throughout. While the immediate fixes above will resolve the response property inconsistencies, completing the axios migration would:

  1. Align with modern JavaScript ecosystem standards (VueResource is no longer maintained for Vue 2)
  2. Be consistent with other modules in the codebase that use axios (e.g., user.js)
  3. Reduce the number of HTTP client libraries in the bundle

This can be deferred if there are time constraints, but should be tracked for a follow-up.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06cfb6a and 77e65a9.

📒 Files selected for processing (10)
  • app/javascript/activities.js (3 hunks)
  • app/javascript/activity.js (1 hunks)
  • app/javascript/application.js (1 hunks)
  • app/javascript/components/activity/ProductTotals.vue (1 hunks)
  • app/javascript/credit_mutations.js (1 hunks)
  • app/javascript/invoices.js (1 hunks)
  • app/javascript/payment_add.js (1 hunks)
  • app/javascript/price_lists.js (5 hunks)
  • app/javascript/user.js (1 hunks)
  • app/javascript/users.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/javascript/credit_mutations.js
  • app/javascript/invoices.js
  • app/javascript/payment_add.js
  • app/javascript/activity.js
🧰 Additional context used
🧬 Code graph analysis (3)
app/javascript/activities.js (1)
app/javascript/price_lists.js (2)
  • element (8-8)
  • priceLists (10-10)
app/javascript/price_lists.js (1)
app/javascript/activities.js (2)
  • element (7-7)
  • priceLists (9-9)
app/javascript/users.js (3)
app/javascript/activity.js (2)
  • vueInstance (7-7)
  • element (18-18)
app/javascript/credit_mutations.js (2)
  • vueInstance (8-8)
  • element (20-20)
app/javascript/invoices.js (2)
  • vueInstance (8-8)
  • element (19-19)
🪛 ESLint
app/javascript/application.js

[error] 6-6: 'axios' is defined but never used.

(no-unused-vars)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (5)
app/javascript/users.js (2)

5-12: LGTM!

The Vue instance tracking and cleanup logic properly handles Turbo's caching mechanism, preventing memory leaks and stale instances. The pattern is consistent with other entrypoints in the codebase.


16-17: LGTM!

The element selection properly checks for null before proceeding, ensuring the Vue initialization only runs on the appropriate page.

app/javascript/user.js (1)

4-12: LGTM! Clean migration to Turbo.

The changes correctly update the import path, migrate from turbolinks:load to turbo:load, and modernize variable declarations from var to const. All changes align with the PR's migration objectives.

app/javascript/activities.js (1)

5-9: Good defensive programming with the element guard.

The migration to turbo:load is correct, and the added null check on line 8 prevents errors when the target element is not present on the page.

app/javascript/price_lists.js (1)

68-88: Fix inconsistent response property access in saveProduct.

The code uses VueResource's this.$http API (lines 68, 77) but mixes response property styles:

  • Lines 69, 81: response.data (axios style)
  • Line 74: error.response?.data?.errors (axios style)
  • Line 86: error.response?.body?.errors (VueResource style)

VueResource uses response.body for success and response.body for errors, not response.data. This inconsistency will cause silent failures where response data is undefined or errors aren't captured correctly.

Apply this diff to use VueResource properties consistently:

 this.$http.put(`/products/${sanitizedProduct.id}.json`, { product: sanitizedProduct }).then((response) => {
-  const newProduct = response.data;
+  const newProduct = response.body;
   newProduct.editing = false;

   this.$set(this.products, this.products.indexOf(product), newProduct);
 }).catch((error) => {
-  this.errors = error.response?.data?.errors || ['An error occurred'];
+  this.errors = error.body?.errors || ['An error occurred'];
 });

Apply the same fix to the post call:

 this.$http.post('/products.json', { product: sanitizedProduct }).then( (response) => {
   const index = this.products.indexOf(product);
   this.products.splice(index, 1);

-  const newProduct = response.data;
+  const newProduct = response.body;
   newProduct.editing = false;

   this.products.push(newProduct);
 }).catch((error) => {
-  this.errors = error.response?.body?.errors || ['An error occurred'];
+  this.errors = error.body?.errors || ['An error occurred'];
 });

Likely an incorrect or invalid review comment.

Comment thread app/javascript/activities.js
Comment thread app/javascript/application.js Outdated
Comment on lines +80 to 87
this.$http.get(`/activities/${this.activity}/product_totals`, { params }).then((response) => {
this.orderTotals = response.body;
this.totalAmount = this.orderTotals.reduce((a, b) => a + parseFloat(b.price), 0.0);
this.isLoading = false;
}).catch((error) => {
this.isLoading = false;
console.error(error);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify VueResource vs axios migration status.

The past review comment mentions axios and shows axios code (marked as addressed in commit 7a5b9c9), and the PR objectives indicate migration away from VueResource. However, this code still uses VueResource's this.$http API and accesses response.body (line 81). Please confirm whether:

  • The axios migration was intentionally reverted or is incomplete for this component
  • VueResource remains a dependency in the project
  • The error handling pattern here aligns with the intended HTTP client library

Run the following script to check the HTTP client library usage across Vue components:


🏁 Script executed:

#!/bin/bash
# Check which HTTP client library is used in Vue components

echo "=== Checking for axios imports/usage in Vue components ==="
rg -n --type=vue -C2 "import.*axios|from.*axios|\baxios\."

echo -e "\n=== Checking for VueResource usage in Vue components ==="
rg -n --type=vue -C2 '\$http\.'

echo -e "\n=== Checking package.json for axios and vue-resource ==="
jq '.dependencies, .devDependencies' package.json | rg -i "axios|vue-resource"

Length of output: 279


Let me run a corrected script to search the Vue files properly:


Wait, let me fix that - I need cloning for this:




🏁 Script executed:

#!/bin/bash
# Search Vue files using proper file path patterns

echo "=== Checking for axios usage in .vue files ==="
rg -n "import.*axios|from.*axios|axios\." app/javascript/components --type=js -g "*.vue"

echo -e "\n=== Checking for VueResource usage in .vue files ==="
rg -n '\$http\.' app/javascript/components -g "*.vue"

echo -e "\n=== List of .vue files in activity directory ==="
fd "\.vue$" app/javascript/components/activity

echo -e "\n=== Checking ProductTotals.vue for both libraries ==="
cat app/javascript/components/activity/ProductTotals.vue | head -100

Length of output: 4015


🏁 Script executed:

#!/bin/bash
# Search Vue files using proper file path patterns

echo "=== Checking for axios usage in .vue files ==="
find app/javascript/components -name "*.vue" -exec grep -l "axios" {} \;

echo -e "\n=== Checking for VueResource usage in .vue files ==="
find app/javascript/components -name "*.vue" -exec grep -l '\$http' {} \;

echo -e "\n=== Count of .vue files using each library ==="
echo "Files using axios:"
find app/javascript/components -name "*.vue" -exec grep -l "axios" {} \; | wc -l
echo "Files using \$http:"
find app/javascript/components -name "*.vue" -exec grep -l '\$http' {} \; | wc -l

echo -e "\n=== Show axios vs VueResource usage pattern ==="
echo "Axios usage:"
find app/javascript/components -name "*.vue" -exec grep -l "axios" {} \; | head -5

echo -e "\nVueResource usage:"
find app/javascript/components -name "*.vue" -exec grep -l '\$http' {} \; | head -5

Length of output: 866


Let me check how error handling is implemented in other components to determine if user-facing feedback is expected:


---

🏁 Script executed:

```shell
#!/bin/bash
# Compare error handling patterns in axios vs VueResource components

echo "=== Error handling in ProductTable.vue (axios) ==="
grep -A5 "\.catch" app/javascript/components/ProductTable.vue

echo -e "\n=== Error handling in UserInput.vue (VueResource) ==="
grep -A5 "\.catch" app/javascript/components/UserInput.vue

echo -e "\n=== Error handling in OrderHistory.vue (axios) ==="
grep -A5 "\.catch" app/javascript/components/user/OrderHistory.vue

echo -e "\n=== Error handling in ActivityOrders.vue (axios) ==="
grep -A5 "\.catch" app/javascript/components/orderscreen/ActivityOrders.vue

Length of output: 211


Let me check the actual HTTP request implementations more broadly:


---

🏁 Script executed:

```shell
#!/bin/bash
# Show HTTP request patterns in different components

echo "=== ProductTotals.vue HTTP request (VueResource, lines 80-87) ==="
sed -n '80,87p' app/javascript/components/activity/ProductTotals.vue

echo -e "\n=== ProductTable.vue HTTP request (axios) ==="
grep -B2 -A8 "axios.patch" app/javascript/components/ProductTable.vue | head -20

echo -e "\n=== UserInput.vue HTTP request (VueResource) ==="
grep -B2 -A8 "this.\$http.post" app/javascript/components/UserInput.vue | head -20

echo -e "\n=== ActivityOrders.vue HTTP request (axios) ==="
grep -B2 -A8 "axios.get" app/javascript/components/orderscreen/ActivityOrders.vue | head -20

echo -e "\n=== Checking if ProductTotals.vue has error handling in original file ==="
grep -n "catch\|error" app/javascript/components/activity/ProductTotals.vue

Length of output: 1981


Add user-facing error feedback to match component patterns in codebase.

The code correctly uses VueResource and implements error handling with console.error() (lines 84-87), but lacks user-facing feedback. Other components in the codebase—such as ProductTable.vue—display user-visible error messages (e.g., "Er is iets misgegaan bij het opslaan van deze rij"). Consider showing a similar message to the user when the product totals request fails, rather than silently logging to console.

The VueResource API usage is intentional as part of the ongoing gradual migration (4 components now use axios, 2 use VueResource; both libraries remain in dependencies).

🤖 Prompt for AI Agents
In app/javascript/components/activity/ProductTotals.vue around lines 80-87, the
catch block only logs the error to console and should show a user-facing error
like other components; add a data property (e.g. errorMessage or errorAlert) and
in the catch set this.errorMessage = "Er is iets misgegaan bij het ophalen van
producttotalen" (or use the same message used in ProductTable.vue), keep
this.isLoading = false and still console.error(error) for debugging, and update
the template to render the error message/alert in the same style/pattern as
ProductTable.vue so users see the failure.

Comment thread app/javascript/users.js
Comment thread app/javascript/users.js Outdated
Comment thread app/javascript/users.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
app/javascript/users.js (1)

1-3: Axios security vulnerabilities still need to be addressed.

The previous review identified HIGH-severity vulnerabilities in axios 1.4.0 (DoS and SSRF/credential leakage). Please upgrade to axios >= 1.13.2 in package.json.

app/javascript/price_lists.js (1)

68-76: Incorrect response property access for VueResource.

Line 69 accesses response.data after a this.$http.put call, but VueResource uses response.body for the response payload, not response.data. Similarly, line 74's error handler uses error.response?.data?.errors, but VueResource errors use error.response.body.

If keeping VueResource, apply this diff:

 this.$http.put(`/products/${sanitizedProduct.id}.json`, { product: sanitizedProduct }).then((response) => {
-  const newProduct = response.data;
+  const newProduct = response.body;
   newProduct.editing = false;
   
   this.$set(this.products, this.products.indexOf(product), newProduct);
 }).catch((error) => {
-  this.errors = error.response?.data?.errors || ['An error occurred'];
+  this.errors = error.response?.body?.errors || ['An error occurred'];
 });

Alternatively, if migrating to axios, replace this.$http with axios and keep response.data.

🧹 Nitpick comments (1)
app/javascript/users.js (1)

19-41: LGTM! Previous critical issue resolved.

The JSON parsing is now wrapped in a try-catch block, addressing the previous review's concern about potential SyntaxError crashes. The error handling prevents page initialization failure when data attributes contain malformed JSON.

Optional enhancement: Consider displaying a user-facing error message (e.g., setting a data property that renders an alert in the UI) in addition to the console logging, so users understand why the table didn't load.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 77e65a9 and eab76c9.

📒 Files selected for processing (5)
  • app/javascript/activities.js (3 hunks)
  • app/javascript/application.js (1 hunks)
  • app/javascript/components/activity/ProductTotals.vue (1 hunks)
  • app/javascript/price_lists.js (5 hunks)
  • app/javascript/users.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/javascript/application.js
🧰 Additional context used
🧬 Code graph analysis (3)
app/javascript/activities.js (1)
app/javascript/price_lists.js (2)
  • element (8-8)
  • priceLists (10-10)
app/javascript/price_lists.js (1)
app/javascript/activities.js (2)
  • element (8-8)
  • priceLists (10-10)
app/javascript/users.js (3)
app/javascript/credit_mutations.js (1)
  • vueInstance (8-8)
app/javascript/invoices.js (1)
  • vueInstance (8-8)
app/javascript/activity.js (1)
  • vueInstance (7-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (9)
app/javascript/components/activity/ProductTotals.vue (1)

80-80: Good use of template literals.

The template literal syntax is cleaner and more maintainable than string concatenation.

app/javascript/users.js (3)

5-5: LGTM!

The module-scoped vueInstance variable follows the consistent pattern used across other entrypoints (credit_mutations.js, invoices.js, activity.js).


7-12: LGTM!

Proper cleanup of the Vue instance before Turbo caches the page prevents memory leaks and stale state issues during navigation.


14-18: LGTM! Previous critical issue resolved.

The CSRF token setup now includes proper null checking, addressing the previous review's concern about potential TypeError when the meta tag is missing.

app/javascript/price_lists.js (2)

142-156: Correct VueResource usage in archive/unarchive methods.

These methods correctly use VueResource patterns: response.body (lines 144, 152) and error.response?.body?.errors (lines 146, 154). This is the correct approach for VueResource, unlike the inconsistent patterns in saveProduct above.


77-88: Incorrect response property access for VueResource (line 81).

Line 81 accesses response.data after a this.$http.post call, but VueResource uses response.body for the response payload. Note that line 86 correctly uses error.response?.body?.errors for the error handler, making the pattern inconsistent within the same function.

Apply this diff:

 this.$http.post('/products.json', { product: sanitizedProduct }).then( (response) => {
   const index = this.products.indexOf(product);
   this.products.splice(index, 1);
 
-  const newProduct = response.data;
+  const newProduct = response.body;
   newProduct.editing = false;
 
   this.products.push(newProduct);
 }).catch((error) => {
   this.errors = error.response?.body?.errors || ['An error occurred'];
 });

Likely an incorrect or invalid review comment.

app/javascript/activities.js (3)

1-7: VueResource usage in a migration PR.

This file imports and uses VueResource (lines 2, 4, 7), which is consistent internally but raises the question of whether VueResource should be retained across the codebase. The PR objectives mention migrating to modern Rails 7 tooling, and past review comments on price_lists.js suggested moving to axios. Consider whether a codebase-wide decision has been made about VueResource vs. axios for HTTP requests.

That said, the code itself is correct: VueResource is properly imported, configured, and the CSRF token setup is appropriate.


6-10: Clean Turbo integration and element guard.

The migration from turbolinks:load to turbo:load (line 6) is correct, and the element existence guard (line 9) prevents errors when the modal isn't present on the page. Well done.


28-28: Improved case-insensitive filtering.

The case-insensitive filtering using .toLowerCase() on both the value name and query is a good UX improvement.

Comment thread app/javascript/components/activity/ProductTotals.vue
Comment thread app/javascript/price_lists.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eab76c9 and 99521b0.

📒 Files selected for processing (3)
  • app/javascript/components/activity/ProductTotals.vue (1 hunks)
  • app/javascript/order_screen.js (8 hunks)
  • app/javascript/price_lists.js (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/javascript/components/activity/ProductTotals.vue
  • app/javascript/price_lists.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (8)
app/javascript/order_screen.js (8)

1-9: LGTM: Imports properly configured for the new pipeline.

The imports correctly reference local components and include both VueResource and axios. This aligns with the migration to js-bundling and addresses the previous review feedback about missing axios.


11-31: LGTM: Turbo lifecycle integration properly implemented.

The migration from turbolinks:load to turbo:load is correct, and CSRF tokens are appropriately configured for both VueResource and axios. The dataset parsing uses const declarations, which is good practice.


65-75: LGTM: User refresh logic correctly uses findIndex.

The updated logic properly uses findIndex with ID comparison to locate and update users, handling both existing and new user scenarios. This correctly addresses the previous review feedback.


176-181: LGTM: Order confirmation user update follows the correct pattern.

Uses findIndex with ID comparison consistently with the rest of the file.


228-240: LGTM: SumUp integration with corrected property name.

The typo fix from affilateKey to affiliateKey is correct, and the platform-specific URL construction logic is properly maintained.


332-348: LGTM: Validation logic properly extracted into a helper method.

The isFormInvalid() helper consolidates validation checks and improves code organization. Its usage in saveCreditMutation() is correct.


357-367: LGTM: Credit mutation user update uses the correct pattern.

Consistently applies findIndex with ID comparison to update the user in the array.


431-431: LGTM: Event listener properly closed.

Comment thread app/javascript/order_screen.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/javascript/order_screen.js (1)

359-377: Fix indentation around setTimeout to satisfy ESLint.

The logic for updating app.users after a credit mutation and hiding the modal is good, but ESLint is complaining about the indentation on the setTimeout block (lines 372–373). Adjust the inner line and closing line so they are indented relative to the callback and the surrounding method:

-            /* eslint-disable no-undef */
-            setTimeout(() => {
-            bootstrap.Modal.getOrCreateInstance('#credit-mutation-modal').hide();
-          }, 0);
+            /* eslint-disable no-undef */
+            setTimeout(() => {
+              bootstrap.Modal.getOrCreateInstance('#credit-mutation-modal').hide();
+            }, 0);

This should clear the reported indent errors while preserving behavior.

♻️ Duplicate comments (1)
app/javascript/order_screen.js (1)

205-221: Fix VueResource error handling: use error.status / error.body instead of error.response.

handleXHRError treats its argument as { response: { status, body }} but VueResource passes the response object directly, so error.response?.status and error.response.body are always undefined. This breaks all status‑based handling and Sentry logging; every error will fall through to "Error undefined?!🤔".

Update the method to read from the response object itself:

-        handleXHRError(error) {
-          if (error.response?.status === 500) {
+        handleXHRError(error) {
+          if (error.status === 500) {
             this.sendFlash('Server error!', 'Herlaad de pagina', 'error');

             try {
-              throw new Error(JSON.stringify(error.response.body));
+              throw new Error(JSON.stringify(error.body));
             } catch(e) {
               /* eslint-disable no-undef */
               Sentry.captureException(e);
               /* eslint-enable no-undef */
             }
-          } else if (error.response?.status === 422) {
+          } else if (error.status === 422) {
             this.sendFlash('Error bij het opslaan!', 'Probeer het opnieuw', 'warning');
           } else {
-            this.sendFlash(`Error ${error.response?.status}?!🤔`, 'Herlaad de pagina', 'info');
+            this.sendFlash(`Error ${error.status}?!🤔`, 'Herlaad de pagina', 'info');
           }
         },
In VueResource 1.5.x, what does the error callback of `this.$http.post(...).then(success, error)` receive—does it get a `Response` object with `.status` and `.body` directly, or an object with a nested `.response` property?
🧹 Nitpick comments (1)
app/javascript/order_screen.js (1)

163-193: Post‑order user update is correct; consider filtering out empty order rows.

Updating the users array via findIndex on user.id and $set with response.body.user is sound and avoids the earlier indexOf brittleness. One small improvement while you’re in here: order_rows_attributes built via map will contain undefined entries for rows with falsy amount. Consider filtering them out (e.g., .filter(Boolean) or building the array with reduce) so you don’t send null entries to the backend.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99521b0 and d06ebd6.

📒 Files selected for processing (2)
  • app/javascript/order_screen.js (9 hunks)
  • app/views/activities/_credit_mutation_modal.html.erb (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/views/activities/_credit_mutation_modal.html.erb
🧰 Additional context used
🪛 ESLint
app/javascript/order_screen.js

[error] 372-372: Expected indentation of 14 spaces but found 12.

(indent)


[error] 373-373: Expected indentation of 12 spaces but found 10.

(indent)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (5)
app/javascript/order_screen.js (5)

4-21: Turbo load init, imports, and dataset parsing look consistent.

The added bootstrap import, switch to turbo:load, and the way you parse datasets into const values and seed Vue state are coherent and don't introduce any obvious issues.


63-85: User refresh logic correctly syncs users and selectedUser.

Using response.body plus findIndex on id and then updating or appending refreshedUser (and reassigning selectedUser when IDs match) makes the user state consistent after a reload without breaking existing behavior.


229-241: SumUp URL construction and dataset usage look fine.

Reading sumupKey / sumupCallback from element.dataset and composing the SumUp URL with iOS vs. other handling is straightforward and consistent; no functional issues stand out here.


333-347: isFormInvalid helper cleanly centralizes credit mutation validation.

The helper’s combination of checkValidity() with explicit checks for user, amount, and description, and wiring it into saveCreditMutation via creditMutationFormInvalid, makes the validation logic clearer without changing behavior.


433-434: Outer Turbo load handler closes correctly.

The final closing braces and parenthesis for the document.addEventListener('turbo:load', ...) wrapper are aligned and consistent; nothing else needed here.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
app/javascript/order_screen.js (1)

205-221: Fix VueResource error response property access.

The error handling accesses error.response?.status and error.response.body, but VueResource error callbacks receive a response object with status and body properties directly—not nested under a .response property. This breaks all error handling: the status checks will always fail because error.response?.status returns undefined, causing every error to fall through to the else case.

Apply this diff to fix the property access:

 handleXHRError(error) {
-  if (error.response?.status === 500) {
+  if (error.status === 500) {
     this.sendFlash('Server error!', 'Herlaad de pagina', 'error');

     try {
-      throw new Error(JSON.stringify(error.response.body));
+      throw new Error(JSON.stringify(error.body));
     } catch(e) {
       /* eslint-disable no-undef */
       Sentry.captureException(e);
       /* eslint-enable no-undef */
     }
-  } else if (error.response?.status === 422) {
+  } else if (error.status === 422) {
     this.sendFlash('Error bij het opslaan!', 'Probeer het opnieuw', 'warning');
   } else {
-    this.sendFlash(`Error ${error.response?.status}?!🤔`, 'Herlaad de pagina', 'info');
+    this.sendFlash(`Error ${error.status}?!🤔`, 'Herlaad de pagina', 'info');
   }
 },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d06ebd6 and b7ee84a.

📒 Files selected for processing (1)
  • app/javascript/order_screen.js (9 hunks)
🧰 Additional context used
🪛 ESLint
app/javascript/order_screen.js

[error] 371-371: Expected indentation of 12 spaces but found 10.

(indent)


[error] 372-372: Expected indentation of 14 spaces but found 12.

(indent)


[error] 373-373: Expected indentation of 12 spaces but found 10.

(indent)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (6)
app/javascript/order_screen.js (6)

1-21: LGTM! Clean migration to Turbo lifecycle.

The imports, CSRF token setup, and migration from turbolinks:load to turbo:load are all correct. The data parsing properly uses const for immutable references.


66-76: LGTM! Robust user refresh logic.

The use of findIndex with ID comparison correctly handles both updating existing users and appending new ones. The selectedUser synchronization ensures consistency.


177-182: LGTM! Consistent user update pattern.

The order success handler correctly updates the user entry using findIndex with ID comparison, matching the pattern used elsewhere in the file.


229-241: LGTM! SumUp integration looks solid.

The payment URL construction correctly reads from dataset, handles iOS vs. other platforms appropriately, and builds the proper SumUp merchant URL.


333-339: LGTM! Good validation refactor.

Extracting the validation logic into isFormInvalid() improves readability and maintainability. The combination of native form validation with custom presence checks is appropriate.


358-368: LGTM! Credit mutation user handling is consistent.

The user array update correctly uses findIndex with ID comparison, and the conditional user selection logic appropriately handles the keepUserSelected flag and empty order rows.

Comment thread app/javascript/order_screen.js Outdated
Comment on lines +371 to +373
setTimeout(() => {
bootstrap.Modal.getOrCreateInstance('#credit-mutation-modal').hide();
}, 0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix indentation to resolve ESLint failure.

Lines 371-373 have incorrect indentation, causing the ESLint check to fail and blocking the pipeline.

Apply this diff:

           /* eslint-disable no-undef */
-          setTimeout(() => {
+            setTimeout(() => {
-            bootstrap.Modal.getOrCreateInstance('#credit-mutation-modal').hide();
+              bootstrap.Modal.getOrCreateInstance('#credit-mutation-modal').hide();
-          }, 0);
+            }, 0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setTimeout(() => {
bootstrap.Modal.getOrCreateInstance('#credit-mutation-modal').hide();
}, 0);
setTimeout(() => {
bootstrap.Modal.getOrCreateInstance('#credit-mutation-modal').hide();
}, 0);
🧰 Tools
🪛 ESLint

[error] 371-371: Expected indentation of 12 spaces but found 10.

(indent)


[error] 372-372: Expected indentation of 14 spaces but found 12.

(indent)


[error] 373-373: Expected indentation of 12 spaces but found 10.

(indent)

🤖 Prompt for AI Agents
In app/javascript/order_screen.js around lines 371 to 373, the setTimeout
callback and its contents are mis-indented causing an ESLint failure; adjust
indentation so the setTimeout line and its inner call are aligned with
surrounding code (use two or four spaces consistent with project style) and
ensure the closing brace and parentheses match the original indentation level to
satisfy ESLint.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/javascript/order_screen.js (1)

358-359: Use findIndex with ID comparison instead of indexOf.

Line 359 uses indexOf which was flagged in previous reviews as brittle—if selectedUser is a refreshed object, indexOf will fail to locate it. Use findIndex with ID comparison for robustness, consistent with the pattern used elsewhere in this file (lines 67, 178).

Apply this diff:

         }).then((response) => {
-          app.$set(app.users, app.users.indexOf(app.selectedUser), response.body.user);
+          const index = app.users.findIndex((candidate) => candidate.id === response.body.user.id);
+          if (index !== -1) {
+            app.$set(app.users, index, response.body.user);
+          }
           if(!app.keepUserSelected && app.orderRows.length === 0){
♻️ Duplicate comments (1)
app/javascript/order_screen.js (1)

205-221: Fix VueResource error response property access.

This critical issue was flagged in previous reviews but remains unaddressed. VueResource error callbacks receive the response object directly—the status and body properties are not nested under .response. With the current code, error.response?.status returns undefined, causing all errors to show "Error undefined?!🤔" instead of the appropriate message.

Apply this diff to fix the property access:

 handleXHRError(error) {
-  if (error.response?.status === 500) {
+  if (error.status === 500) {
     this.sendFlash('Server error!', 'Herlaad de pagina', 'error');

     try {
-      throw new Error(JSON.stringify(error.response.body));
+      throw new Error(JSON.stringify(error.body));
     } catch(e) {
       /* eslint-disable no-undef */
       Sentry.captureException(e);
       /* eslint-enable no-undef */
     }
-  } else if (error.response?.status === 422) {
+  } else if (error.status === 422) {
     this.sendFlash('Error bij het opslaan!', 'Probeer het opnieuw', 'warning');
   } else {
-    this.sendFlash(`Error ${error.response?.status}?!🤔`, 'Herlaad de pagina', 'info');
+    this.sendFlash(`Error ${error.status}?!🤔`, 'Herlaad de pagina', 'info');
   }
 },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7ee84a and bcfbf3a.

📒 Files selected for processing (1)
  • app/javascript/order_screen.js (8 hunks)
🧰 Additional context used
🪛 ESLint
app/javascript/order_screen.js

[error] 368-368: Expected indentation of 12 spaces but found 10.

(indent)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build

Comment thread app/javascript/order_screen.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
app/javascript/order_screen.js (2)

336-373: Fix credit mutation user update robustness and ESLint indentation.

Two issues in this block:

  1. Brittle indexOf without guard:

    • app.$set(app.users, app.users.indexOf(app.selectedUser), response.body.user); assumes selectedUser is always present in users.
    • If it’s ever missing (e.g., list reloaded independently), indexOf returns -1 and Vue.set will mutate the wrong entry.
    • Prefer an id-based findIndex with an index !== -1 guard, consistent with the order-confirmation flow.
  2. Indentation breaks ESLint (line 361):

    • The bootstrap.Modal.getOrCreateInstance(...).hide(); line is under-indented relative to the surrounding block, matching the reported ESLint error.
    • Adjusting indentation by two spaces will resolve the lint failure.

Apply this diff:

       }).then((response) => {
-            app.$set(app.users, app.users.indexOf(app.selectedUser), response.body.user);
+            const index = app.users.findIndex(
+              (candidate) => candidate.id === response.body.user.id
+            );
+            if (index !== -1) {
+              app.$set(app.users, index, response.body.user);
+            }
@@
-            /* eslint-disable no-undef */
-          bootstrap.Modal.getOrCreateInstance('#credit-mutation-modal').hide();
+            /* eslint-disable no-undef */
+            bootstrap.Modal.getOrCreateInstance('#credit-mutation-modal').hide();

(Indentation assumes 2 extra spaces to align with other statements in this .then block.)


205-221: VueResource error handling still reads from error.response, breaking all status checks.

For VueResource, the rejected promise handler receives the response object directly (status, body, …), not nested under error.response. As written, error.response?.status is always undefined, so:

  • 500/422 branches are never taken,
  • the fallback message shows Error undefined?!🤔,
  • the Sentry payload uses error.response.body, which is also undefined.

Switch to error.status and error.body so the handler actually reflects the HTTP response.

Apply this diff:

-        handleXHRError(error) {
-          if (error.response?.status === 500) {
+        handleXHRError(error) {
+          if (error.status === 500) {
             this.sendFlash('Server error!', 'Herlaad de pagina', 'error');
 
             try {
-              throw new Error(JSON.stringify(error.response.body));
+              throw new Error(JSON.stringify(error.body));
             } catch(e) {
               /* eslint-disable no-undef */
               Sentry.captureException(e);
               /* eslint-enable no-undef */
             }
-          } else if (error.response?.status === 422) {
+          } else if (error.status === 422) {
             this.sendFlash('Error bij het opslaan!', 'Probeer het opnieuw', 'warning');
           } else {
-            this.sendFlash(`Error ${error.response?.status}?!🤔`, 'Herlaad de pagina', 'info');
+            this.sendFlash(`Error ${error.status}?!🤔`, 'Herlaad de pagina', 'info');
           }
         },

If you have customized this.$http away from VueResource semantics, please double-check the response shape in your docs or devtools.

What is the shape of the error object passed to VueResource's promise rejection handler, and does it expose `status` and `body` directly?
🧹 Nitpick comments (2)
app/views/activities/_credit_mutation_modal.html.erb (2)

19-20: Preserve accessibility semantics for the currency icon

Switching from the fa_icon helper to a raw <i> likely dropped default aria-hidden behavior. For a purely decorative currency icon inside an input group, it’s better to hide it from screen readers.

-                <div class="input-group-text">
-                  <i class="fas fa-euro-sign"></i>
-                </div>
+                <div class="input-group-text">
+                  <i class="fas fa-euro-sign" aria-hidden="true"></i>
+                </div>

62-64: Confirm UX when auto‑dismissing the modal on save click

Adding data-bs-dismiss="modal" means the modal will close immediately on click, regardless of what saveCreditMutation does. If that method performs async work or can fail/validate client-side, users may never see errors or get a chance to correct input before the dialog disappears.

Also note that this button is outside the <form>, so type="submit" will not trigger native form submission/validation unless you’re handling it manually elsewhere.

Consider either:

  • Letting saveCreditMutation control when to close the modal (remove data-bs-dismiss="modal" and programmatically hide the modal on success), and/or
  • Associating the button with the form (moving it inside the <form> or adding form="credit-mutation-modal-form") if you want native validation semantics.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bcfbf3a and 5afa6ab.

📒 Files selected for processing (4)
  • app/javascript/components/UserInput.vue (3 hunks)
  • app/javascript/credit_mutations.js (2 hunks)
  • app/javascript/order_screen.js (8 hunks)
  • app/views/activities/_credit_mutation_modal.html.erb (2 hunks)
🧰 Additional context used
🪛 ESLint
app/javascript/order_screen.js

[error] 361-361: Expected indentation of 12 spaces but found 10.

(indent)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (7)
app/javascript/order_screen.js (4)

4-21: Turbo bootstrap setup and data extraction look solid.

Importing Bootstrap as a module and wiring the turbo:load listener with CSRF header setup and JSON dataset parsing is consistent and correct for the new js-bundling/Turbo pipeline; no changes needed here.


63-76: User refresh logic on setUser is robust.

Refreshing the user via this.$http.get, updating the users array by id, pushing missing users, and re-pointing selectedUser when ids match correctly keeps local state in sync with the server while avoiding stale selections or race conditions.


177-181: Order confirmation correctly updates the user entry.

Using findIndex on this.users with an id comparison plus an index !== -1 guard is a safe way to update the user after an order; this avoids the brittle indexOf(selectedUser) pattern and prevents accidental writes when the user is missing.


229-234: SumUp URL construction and affiliate key rename look correct.

Using affiliate-key=${affiliateKey} with affiliateKey sourced from element.dataset.sumupKey and splitting iOS vs. other platforms (amount vs total, dedicated success/fail callbacks) preserves the previous behavior while adopting the new naming; this looks good.

app/javascript/components/UserInput.vue (2)

46-71: Debounced input handling and empty-query reset look good.

The debounceTimer pattern (clearTimeout on each updateValue call, setTimeout for updateSuggestions) plus the early-return path that clears selectedSuggestion and suggestions when query === '' is clean and should avoid both flicker and stale results.


94-114: Suggestion click handling and debounce cleanup are correct.

suggestionClicked correctly syncs selectedSuggestion, query, dropdown state, and emits the selection, and beforeDestroy clearing debounceTimer prevents stray updateSuggestions executions after component teardown.

app/javascript/credit_mutations.js (1)

4-20: Credit mutations entrypoint aligns correctly with Turbo and new component path.

Switching to turbo:load, importing UserInput from ./components/UserInput.vue, and using const element while guarding on element != null is all consistent with the new asset structure and VueResource setup.

Comment thread app/javascript/components/UserInput.vue

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
app/javascript/order_screen.js (2)

176-180: Consider adding defensive push for consistency.

The user update after order placement only updates if found, while the pattern at lines 68-72 (in setUser) also pushes if not found. For consistency and defensive programming, consider adding an else branch:

 if (user) {
   const index = this.users.findIndex((candidate) => candidate.id === user.id);
   if (index !== -1) {
     this.$set(this.users, index, response.body.user);
+  } else {
+    this.users.push(response.body.user);
   }
 }

In practice, the user should already be in the array when placing orders, but this ensures robustness if assumptions change.


349-353: Consider adding defensive push for consistency.

Similar to the order confirmation flow, this credit mutation save only updates the user if found. For consistency with the pattern at lines 68-72, consider handling the not-found case:

 const index = app.users.findIndex((candidate) => candidate.id === response.body.user.id);
 if (index !== -1) {
   app.$set(app.users, index, response.body.user);
+} else {
+  app.users.push(response.body.user);
 }

This ensures the users array remains consistent even if assumptions about pre-existing users change.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5afa6ab and a4be573.

📒 Files selected for processing (3)
  • .gitattributes (1 hunks)
  • app/javascript/order_screen.js (8 hunks)
  • spec/controllers/orders_controller/update_spec.rb (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .gitattributes
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Lint
  • GitHub Check: Test
🔇 Additional comments (5)
spec/controllers/orders_controller/update_spec.rb (1)

14-21: Frontend code correctly updated to match new nested parameter structure.

Verification confirms that the frontend code in app/javascript/components/ProductTable.vue (line 88) correctly sends PATCH requests with the nested parameter structure: axios.patch(/orders/${id}, { order: { order_rows_attributes: [...] } }). This matches the expected parameter format in the test file and is consistent with both POST requests (in order_screen.js) and PATCH requests for order updates. The breaking change to the API contract has been properly implemented across the frontend.

app/javascript/order_screen.js (4)

12-21: LGTM! Turbo migration and data extraction.

The migration from turbolinks:load to turbo:load is correct, and the data extraction pattern using const with proper null checks is solid.


66-76: LGTM! Robust user refresh logic.

The pattern of finding by ID and pushing if not found ensures the users array stays consistent after reload. This defensive approach is good practice.


204-220: LGTM! Error handling correctly accesses VueResource properties.

The error handling now correctly uses error.status and error.body for VueResource error responses, with appropriate user feedback and Sentry logging for server errors.


228-240: LGTM! SumUp integration with correct variable naming.

The affiliateKey variable naming is now consistent with JavaScript conventions, and the iOS vs non-iOS callback handling is clear.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/javascript/components/UserInput.vue (2)

73-90: Suggestion list building is safer; consider stable keys for synthetic options.

Initializing results and assigning this.suggestions once is a good improvement and avoids transient inconsistent state. One minor follow‑up: the “Gepind” / “Contant betaald” entries still have no id while the template uses :key="suggestion.id", which can lead to undefined/duplicate keys when these appear.

If it’s safe for the backend, you could give these entries a stable id:

-        if (this.includePin && 'gepind'.indexOf(this.query.toLowerCase()) >= 0) {
-          results.push({ name: 'Gepind', paid_with_pin: true });
-        }
+        if (this.includePin && 'gepind'.indexOf(this.query.toLowerCase()) >= 0) {
+          results.push({ id: '__pin', name: 'Gepind', paid_with_pin: true });
+        }
@@
-        if (this.includeCash && 'contant betaald'.indexOf(this.query.toLowerCase()) >= 0) {
-          results.push({ name: 'Contant betaald', paid_with_cash: true });
-        }
+        if (this.includeCash && 'contant betaald'.indexOf(this.query.toLowerCase()) >= 0) {
+          results.push({ id: '__cash', name: 'Contant betaald', paid_with_cash: true });
+        }

Alternatively, you can keep the payloads as‑is and adjust the :key to fall back to suggestion.name outside this hunk.


94-100: suggestionClicked flow is fine; comments could be tightened.

The click/enter path correctly updates selectedSuggestion, syncs query, closes the dropdown, and emits the value. The inline comments like “this logic is still correct” don’t add much and may become misleading over time; consider either removing them or replacing them with a short domain‑level note (e.g. what the parent can expect from the emitted payload).

Also applies to: 102-102

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4be573 and d12cb43.

📒 Files selected for processing (2)
  • app/javascript/components/UserInput.vue (3 hunks)
  • app/javascript/order_screen.js (8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (7)
app/javascript/components/UserInput.vue (1)

46-48: Debounce implementation and cleanup look solid.

Clearing the timeout on each input and again in beforeDestroy avoids overlapping calls and stray callbacks; keeping debounceTimer internal to the component keeps the public API unchanged.

Also applies to: 59-60, 64-65, 68-71, 111-116

app/javascript/order_screen.js (6)

1-8: LGTM! Imports properly updated for the new bundler.

The imports have been correctly migrated: axios is now imported, bootstrap is available, and component paths use the appropriate relative prefixes for js-bundling.


12-21: LGTM! Clean Turbo migration and improved variable declarations.

The event listener has been correctly updated from turbolinks:load to turbo:load, and the data initialization now properly uses const declarations for immutability.


66-76: LGTM! Robust user refresh logic.

The user refresh flow correctly uses findIndex with ID comparison to update existing users or append new ones, and properly synchronizes selectedUser when the refreshed data matches.


206-222: LGTM! Error handling correctly updated for VueResource.

The error handling now properly accesses error.status and error.body directly, which is correct for VueResource error callbacks.


230-242: LGTM! SumUp integration variable naming corrected.

The typo has been fixed from affilateKey to affiliateKey, and the URL construction logic correctly uses the updated variable name.


351-357: LGTM! Consistent user update pattern.

The credit mutation handler correctly uses the same robust findIndex pattern to update or append the user, maintaining consistency across the codebase.

Comment thread app/javascript/order_screen.js
@lodewiges
lodewiges merged commit 5c9e3c6 into staging Nov 21, 2025
5 of 6 checks passed
@lodewiges
lodewiges deleted the update/migrateWebpackerToJSBundeling branch November 21, 2025 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants