Migrate to Bootstrap 5 and remove Vue compat plugin
Complete migration from Bootstrap 4 (bootstrap-vue) to Bootstrap 5
(bootstrap-vue-next) and remove the @vue/compat plugin to finalize
the Vue 3 migration.
Bundle size impact:
- Before (Bootstrap 4 + bootstrap-vue): 535 KiB gzipped
- After (Bootstrap 5 + bootstrap-vue-next): 511 KiB gzipped
- Reduction: 24 KiB (4.5% smaller)
Package updates:
- Update bootstrap 4.6.2 -> 5.3.8
- Update bootstrap-vue 2.23.1 -> bootstrap-vue-next 0.40.8
- Remove @vue/compat plugin
- Update vue 3.4.29 -> 3.5.24 and related packages
- Add mitt 3.0.1 for global event bus
- Add vue-demi 0.14.10 for library compatibility
Bootstrap 5 CSS updates:
- Replace directional classes: ml/mr/pl/pr -> ms/me/ps/pe
- Replace text-left/right -> text-start/end
- Replace sr-only -> visually-hidden / visually-hidden-focusable
- Update media breakpoint xs -> sm (Bootstrap 5 removed xs)
- Update color functions: gray("700") -> $gray-700
- Add form-switch border-radius for curved toggles
- Update alert, table, toast, form, and button styles
Bootstrap-Vue-Next API changes:
- Use createBootstrap() for plugin registration
- Update modal footer slots: #modal-footer -> #footer
- Fix form select events: @change -> @update:model-value
- Add v-model bindings to modals instead of manual show()/hide()
- Update toast system with custom plugin wrapping useToast()
- Register components and directives explicitly
Vue 3 specific updates:
- Replace $root.$emit with mitt event bus (eventBus.js)
- Update render function from h(App) to createApp(App)
- Add emits option to components
- Use h() instead of $createElement in mixins
- Add Vue 3 compile-time feature flags with documentation
- Update event listeners: $on/$off to eventBus methods
- Add beforeUnmount cleanup for event listeners
New components and significant additions:
- src/plugins/toast.js - Custom toast plugin wrapping useToast() for
Options API compatibility
- src/components/Global/ConfirmModal.vue - Global confirmation dialog
shim to replace Bootstrap 4's removed bvModal.msgBoxConfirm
- src/eventBus.js - mitt-based event bus with Vue 2-compatible API
- Navigation state preservation on page refresh implemented
Critical fixes:
- Add global API interceptor to strip Vue reactivity from payloads
- Preserve binary data (File, Blob, FormData) in API requests
- Fix Generate CSR modal v-model binding for proper open/close
- Remove debug logging and fix jest configuration
- Fix responsive text visibility in AppHeader
- Update BVTableSelectableMixin for proper row selection
- Fix BVToastMixin VNode rendering for Vue 3
Vue 3 modal fixes (lazy-loaded components):
- Add v-model support to network modals (ModalIpv4, ModalIpv6, ModalDns,
ModalHostname, ModalMacAddress, ModalDefaultGateway) by adding
modelValue prop, watcher on modelValue that triggers show(), and
update:modelValue emit in resetForm
- Remove lazy loading from TableIpv4, TableIpv6, TableDns to ensure
modal component refs are available when v-model triggers
- Fix modal title accessibility by adding title prop to modals
(ModalAddDestination, ModalUser, ModalAddRoleGroup, etc.)
i18n fixes (computed properties):
- Fix computed properties using i18n translations in ModalAddRoleGroup,
ModalUser, and ModalUploadCertificate
- Move useI18n() call from data() to setup() and return i18n object
- Use i18n.t() instead of $t in computed properties and templates
- Prevents "this.$t is not a function" and "_ctx.$t is not a function"
errors in Vue 3
Toast notification fixes:
- Fix toast progress bar visibility by setting progressProps to
undefined (documented way to opt-out) instead of false
- Change modelValue prop to interval for auto-dismiss timing
- Remove temporary CSS display:none hack from _toasts.scss
Network settings fixes:
- Fix checkbox @change event sending Vue reactive proxy object instead
of boolean by casting with !! operator in changeDomainNameState and
related methods in NetworkGlobalSettings.vue
- Ensures API receives plain boolean values in PATCH requests
Navigation fixes:
- Fix nav-link styling for navigation items without children by
replacing b-nav-item with router-link in AppNavigation.vue
- Prevents blue font color from .nav-link CSS class
Configuration updates:
- Remove vue-compat webpack configuration
- Add Vue 3 feature flags (__VUE_OPTIONS_API__, etc.)
- Add .cursor to .gitignore
Accessibility improvements:
- Add autocomplete attributes to password and credential inputs
- Add modal title props for screen reader support
Build completes successfully and UI behavior matches pre-migration.
Extracted features (to be submitted in follow-up PRs):
The following features were removed from this migration PR to keep it
focused on the Bootstrap 5 upgrade. They will be submitted separately:
1. UnresponsiveModal - Server connectivity watchdog with auto-retry
2. Auth token persistence - sessionStorage support for X-Auth-Token
3. Hardware store error handling - try/catch, dynamic discovery
4. Login page connecting indicator - Backend polling with spinner
5. Test updates - Jest setup and snapshot updates for
Bootstrap-Vue-Next
6. Documentation updates - Vue 3 and Vue I18n v9+ API documentation
7. Enhanced ConfirmModal - Feature-rich confirmation dialog with
custom actions
Change-Id: Ib76a58f324b3c926cf536e6e4626e4271639de38
Signed-off-by: Jason Westover <jwestover@nvidia.com>
diff --git a/.gitignore b/.gitignore
index df0df9b..08d6585 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@
# Editor directories and files
.idea
.vscode
+.cursor
*.suo
*.ntvs*
*.njsproj
diff --git a/package-lock.json b/package-lock.json
index fa0d598..af2a30f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,23 +10,24 @@
"dependencies": {
"@carbon/icons-vue": "10.49.1",
"@novnc/novnc": "1.3.0",
- "@vue/compat": "3.4.29",
"@vue/vue3-jest": "27.0.0",
"@vuelidate/core": "2.0.3",
"@vuelidate/validators": "2.0.4",
"axios": "1.6.0",
"axios-cache-interceptor": "1.5.1",
- "bootstrap": "4.6.2",
- "bootstrap-vue": "2.23.1",
+ "bootstrap": "5.3.8",
+ "bootstrap-vue-next": "0.40.8",
"core-js": "3.23.4",
"date-fns": "2.28.0",
"date-fns-tz": "1.3.6",
"js-cookie": "3.0.1",
"lodash": "4.17.21",
- "vue": "3.4.29",
- "vue-i18n": "10.0.5",
+ "mitt": "3.0.1",
+ "vue": "3.5.24",
+ "vue-demi": "0.14.10",
+ "vue-i18n": "10.0.8",
"vue-loader": "17.4.2",
- "vue-router": "4.4.0",
+ "vue-router": "4.6.3",
"vuelidate": "0.7.7",
"vuex": "4.1.0",
"webpack-cli": "5.1.4",
@@ -41,7 +42,7 @@
"@vue/cli-plugin-unit-jest": "5.0.8",
"@vue/cli-plugin-vuex": "5.0.8",
"@vue/cli-service": "5.0.8",
- "@vue/compiler-sfc": "3.4.21",
+ "@vue/compiler-sfc": "3.5.22",
"@vue/eslint-config-prettier": "9.0.0",
"@vue/test-utils": "2.4.5",
"autoprefixer": "9.8.6",
@@ -82,19 +83,6 @@
"node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22"
}
},
- "node_modules/@ampproject/remapping": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
- "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@@ -110,30 +98,30 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz",
- "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
- "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"license": "MIT",
"dependencies": {
- "@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
+ "@babel/generator": "^7.28.5",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-module-transforms": "^7.28.3",
- "@babel/helpers": "^7.28.3",
- "@babel/parser": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
"@babel/template": "^7.27.2",
- "@babel/traverse": "^7.28.3",
- "@babel/types": "^7.28.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -149,13 +137,13 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
- "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.3",
- "@babel/types": "^7.28.2",
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -194,18 +182,18 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz",
- "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz",
+ "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.27.3",
- "@babel/helper-member-expression-to-functions": "^7.27.1",
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
"@babel/helper-optimise-call-expression": "^7.27.1",
"@babel/helper-replace-supers": "^7.27.1",
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
- "@babel/traverse": "^7.28.3",
+ "@babel/traverse": "^7.28.5",
"semver": "^6.3.1"
},
"engines": {
@@ -216,14 +204,14 @@
}
},
"node_modules/@babel/helper-create-regexp-features-plugin": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz",
- "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz",
+ "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.27.1",
- "regexpu-core": "^6.2.0",
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "regexpu-core": "^6.3.1",
"semver": "^6.3.1"
},
"engines": {
@@ -260,14 +248,14 @@
}
},
"node_modules/@babel/helper-member-expression-to-functions": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
- "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz",
+ "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
@@ -385,9 +373,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
- "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -418,25 +406,25 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
- "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
"license": "MIT",
"dependencies": {
"@babel/template": "^7.27.2",
- "@babel/types": "^7.28.2"
+ "@babel/types": "^7.28.4"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
- "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.2"
+ "@babel/types": "^7.28.5"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -446,14 +434,14 @@
}
},
"node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz",
- "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz",
+ "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
- "@babel/traverse": "^7.27.1"
+ "@babel/traverse": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
@@ -932,9 +920,9 @@
}
},
"node_modules/@babel/plugin-transform-block-scoping": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz",
- "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz",
+ "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -982,9 +970,9 @@
}
},
"node_modules/@babel/plugin-transform-classes": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz",
- "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==",
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz",
+ "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -993,7 +981,7 @@
"@babel/helper-globals": "^7.28.0",
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-replace-supers": "^7.27.1",
- "@babel/traverse": "^7.28.3"
+ "@babel/traverse": "^7.28.4"
},
"engines": {
"node": ">=6.9.0"
@@ -1020,14 +1008,14 @@
}
},
"node_modules/@babel/plugin-transform-destructuring": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz",
- "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz",
+ "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
- "@babel/traverse": "^7.28.0"
+ "@babel/traverse": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
@@ -1120,9 +1108,9 @@
}
},
"node_modules/@babel/plugin-transform-exponentiation-operator": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz",
- "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz",
+ "integrity": "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1219,9 +1207,9 @@
}
},
"node_modules/@babel/plugin-transform-logical-assignment-operators": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz",
- "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz",
+ "integrity": "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1284,16 +1272,16 @@
}
},
"node_modules/@babel/plugin-transform-modules-systemjs": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz",
- "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz",
+ "integrity": "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-module-transforms": "^7.28.3",
"@babel/helper-plugin-utils": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.27.1"
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
@@ -1385,9 +1373,9 @@
}
},
"node_modules/@babel/plugin-transform-object-rest-spread": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz",
- "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==",
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz",
+ "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1395,7 +1383,7 @@
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/plugin-transform-destructuring": "^7.28.0",
"@babel/plugin-transform-parameters": "^7.27.7",
- "@babel/traverse": "^7.28.0"
+ "@babel/traverse": "^7.28.4"
},
"engines": {
"node": ">=6.9.0"
@@ -1438,9 +1426,9 @@
}
},
"node_modules/@babel/plugin-transform-optional-chaining": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz",
- "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz",
+ "integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1522,9 +1510,9 @@
}
},
"node_modules/@babel/plugin-transform-regenerator": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz",
- "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==",
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz",
+ "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1571,9 +1559,9 @@
}
},
"node_modules/@babel/plugin-transform-runtime": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz",
- "integrity": "sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.5.tgz",
+ "integrity": "sha512-20NUVgOrinudkIBzQ2bNxP08YpKprUkRTiRSd2/Z5GOdPImJGkoN4Z7IQe1T5AdyKI1i5L6RBmluqdSzvaq9/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1740,17 +1728,17 @@
}
},
"node_modules/@babel/preset-env": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz",
- "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.5.tgz",
+ "integrity": "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.28.0",
+ "@babel/compat-data": "^7.28.5",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-validator-option": "^7.27.1",
- "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1",
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5",
"@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
@@ -1763,42 +1751,42 @@
"@babel/plugin-transform-async-generator-functions": "^7.28.0",
"@babel/plugin-transform-async-to-generator": "^7.27.1",
"@babel/plugin-transform-block-scoped-functions": "^7.27.1",
- "@babel/plugin-transform-block-scoping": "^7.28.0",
+ "@babel/plugin-transform-block-scoping": "^7.28.5",
"@babel/plugin-transform-class-properties": "^7.27.1",
"@babel/plugin-transform-class-static-block": "^7.28.3",
- "@babel/plugin-transform-classes": "^7.28.3",
+ "@babel/plugin-transform-classes": "^7.28.4",
"@babel/plugin-transform-computed-properties": "^7.27.1",
- "@babel/plugin-transform-destructuring": "^7.28.0",
+ "@babel/plugin-transform-destructuring": "^7.28.5",
"@babel/plugin-transform-dotall-regex": "^7.27.1",
"@babel/plugin-transform-duplicate-keys": "^7.27.1",
"@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1",
"@babel/plugin-transform-dynamic-import": "^7.27.1",
"@babel/plugin-transform-explicit-resource-management": "^7.28.0",
- "@babel/plugin-transform-exponentiation-operator": "^7.27.1",
+ "@babel/plugin-transform-exponentiation-operator": "^7.28.5",
"@babel/plugin-transform-export-namespace-from": "^7.27.1",
"@babel/plugin-transform-for-of": "^7.27.1",
"@babel/plugin-transform-function-name": "^7.27.1",
"@babel/plugin-transform-json-strings": "^7.27.1",
"@babel/plugin-transform-literals": "^7.27.1",
- "@babel/plugin-transform-logical-assignment-operators": "^7.27.1",
+ "@babel/plugin-transform-logical-assignment-operators": "^7.28.5",
"@babel/plugin-transform-member-expression-literals": "^7.27.1",
"@babel/plugin-transform-modules-amd": "^7.27.1",
"@babel/plugin-transform-modules-commonjs": "^7.27.1",
- "@babel/plugin-transform-modules-systemjs": "^7.27.1",
+ "@babel/plugin-transform-modules-systemjs": "^7.28.5",
"@babel/plugin-transform-modules-umd": "^7.27.1",
"@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1",
"@babel/plugin-transform-new-target": "^7.27.1",
"@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1",
"@babel/plugin-transform-numeric-separator": "^7.27.1",
- "@babel/plugin-transform-object-rest-spread": "^7.28.0",
+ "@babel/plugin-transform-object-rest-spread": "^7.28.4",
"@babel/plugin-transform-object-super": "^7.27.1",
"@babel/plugin-transform-optional-catch-binding": "^7.27.1",
- "@babel/plugin-transform-optional-chaining": "^7.27.1",
+ "@babel/plugin-transform-optional-chaining": "^7.28.5",
"@babel/plugin-transform-parameters": "^7.27.7",
"@babel/plugin-transform-private-methods": "^7.27.1",
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
"@babel/plugin-transform-property-literals": "^7.27.1",
- "@babel/plugin-transform-regenerator": "^7.28.3",
+ "@babel/plugin-transform-regenerator": "^7.28.4",
"@babel/plugin-transform-regexp-modifiers": "^7.27.1",
"@babel/plugin-transform-reserved-words": "^7.27.1",
"@babel/plugin-transform-shorthand-properties": "^7.27.1",
@@ -1840,9 +1828,9 @@
}
},
"node_modules/@babel/runtime": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz",
- "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+ "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1864,17 +1852,17 @@
}
},
"node_modules/@babel/traverse": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
- "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
+ "@babel/generator": "^7.28.5",
"@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.3",
+ "@babel/parser": "^7.28.5",
"@babel/template": "^7.27.2",
- "@babel/types": "^7.28.2",
+ "@babel/types": "^7.28.5",
"debug": "^4.3.1"
},
"engines": {
@@ -1882,13 +1870,13 @@
}
},
"node_modules/@babel/types": {
- "version": "7.28.2",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
- "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1"
+ "@babel/helper-validator-identifier": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
@@ -1901,9 +1889,9 @@
"license": "MIT"
},
"node_modules/@carbon/icon-helpers": {
- "version": "10.65.0",
- "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.65.0.tgz",
- "integrity": "sha512-QsYaZSD33vmE/uAX8EmNO095btGNSasbQj8ETOj+PLGya9xMUrFAWY/GEYM2w9qZRBTrbcrXn61rnBVT2DWbKg==",
+ "version": "10.68.0",
+ "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.68.0.tgz",
+ "integrity": "sha512-fYor/Fs0RLtPMh2KRyPYR83JKoWbAmoQ7tCIW0QQcwYcIu82tvy7uT8vTNhda21w3uueOp70p6rYyY6vYjuRZA==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -2014,22 +2002,22 @@
"license": "BSD-3-Clause"
},
"node_modules/@ibm/telemetry-js": {
- "version": "1.10.1",
- "resolved": "https://registry.npmjs.org/@ibm/telemetry-js/-/telemetry-js-1.10.1.tgz",
- "integrity": "sha512-aQcDq03BKbgNnkHujHbA950wJVHtJnY0PsMHKyxgRvr3XPgInWjDfgxJQmKIp9I8Vy2wpfe0cGmduJfIX0HewQ==",
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/@ibm/telemetry-js/-/telemetry-js-1.10.2.tgz",
+ "integrity": "sha512-F8+/NNUwtm8BuFz18O9KPvIFTFDo8GUSoyhPxPjEpk7nEyEzWGfhIiEPhL00B2NdHRLDSljh3AiCfSnL/tutiQ==",
"license": "Apache-2.0",
"bin": {
"ibmtelemetry": "dist/collect.js"
}
},
"node_modules/@intlify/core-base": {
- "version": "10.0.5",
- "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.5.tgz",
- "integrity": "sha512-F3snDTQs0MdvnnyzTDTVkOYVAZOE/MHwRvF7mn7Jw1yuih4NrFYLNYIymGlLmq4HU2iIdzYsZ7f47bOcwY73XQ==",
+ "version": "10.0.8",
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.8.tgz",
+ "integrity": "sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==",
"license": "MIT",
"dependencies": {
- "@intlify/message-compiler": "10.0.5",
- "@intlify/shared": "10.0.5"
+ "@intlify/message-compiler": "10.0.8",
+ "@intlify/shared": "10.0.8"
},
"engines": {
"node": ">= 16"
@@ -2039,12 +2027,12 @@
}
},
"node_modules/@intlify/message-compiler": {
- "version": "10.0.5",
- "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.5.tgz",
- "integrity": "sha512-6GT1BJ852gZ0gItNZN2krX5QAmea+cmdjMvsWohArAZ3GmHdnNANEcF9JjPXAMRtQ6Ux5E269ymamg/+WU6tQA==",
+ "version": "10.0.8",
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.8.tgz",
+ "integrity": "sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==",
"license": "MIT",
"dependencies": {
- "@intlify/shared": "10.0.5",
+ "@intlify/shared": "10.0.8",
"source-map-js": "^1.0.2"
},
"engines": {
@@ -2055,9 +2043,9 @@
}
},
"node_modules/@intlify/shared": {
- "version": "10.0.5",
- "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.5.tgz",
- "integrity": "sha512-bmsP4L2HqBF6i6uaMqJMcFBONVjKt+siGluRq4Ca4C0q7W2eMaVZr8iCgF9dKbcVXutftkC7D6z2SaSMmLiDyA==",
+ "version": "10.0.8",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.8.tgz",
+ "integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==",
"license": "MIT",
"engines": {
"node": ">= 16"
@@ -2085,9 +2073,9 @@
}
},
"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
- "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2098,9 +2086,9 @@
}
},
"node_modules/@isaacs/cliui/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2136,9 +2124,9 @@
}
},
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2543,6 +2531,16 @@
"@jridgewell/trace-mapping": "^0.3.24"
}
},
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@@ -2569,9 +2567,9 @@
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.30",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
- "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -2663,40 +2661,6 @@
"integrity": "sha512-tR87mY5ADtaELadmZfW937JO/p8fRdz3wkPoqwhqB/vY1XnTQeLSWwkp4yMlr4iIDY0iCficfzFYX5EHMh4MHw==",
"license": "MPL-2.0"
},
- "node_modules/@nuxt/opencollective": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.3.3.tgz",
- "integrity": "sha512-6IKCd+gP0HliixqZT/p8nW3tucD6Sv/u/eR2A9X4rxT/6hXlMzA4GZQzq4d2qnBAwSwGpmKyzkyTjNjrhaA25A==",
- "license": "MIT",
- "dependencies": {
- "chalk": "^4.1.0",
- "consola": "^2.15.0",
- "node-fetch": "^2.6.7"
- },
- "bin": {
- "opencollective": "bin/opencollective.js"
- },
- "engines": {
- "node": ">=8.0.0",
- "npm": ">=5.0.0"
- }
- },
- "node_modules/@nuxt/opencollective/node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
"node_modules/@one-ini/wasm": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz",
@@ -2735,6 +2699,17 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@sideway/address": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
@@ -2962,21 +2937,21 @@
"license": "MIT"
},
"node_modules/@types/express": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz",
- "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.5.tgz",
+ "integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
- "@types/serve-static": "*"
+ "@types/serve-static": "^1"
}
},
"node_modules/@types/express-serve-static-core": {
- "version": "5.0.7",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz",
- "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz",
+ "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2986,6 +2961,29 @@
"@types/send": "*"
}
},
+ "node_modules/@types/express/node_modules/@types/send": {
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz",
+ "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/express/node_modules/@types/serve-static": {
+ "version": "1.15.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz",
+ "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-errors": "*",
+ "@types/node": "*",
+ "@types/send": "<1"
+ }
+ },
"node_modules/@types/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
@@ -3028,9 +3026,9 @@
"license": "MIT"
},
"node_modules/@types/http-proxy": {
- "version": "1.17.16",
- "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz",
- "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==",
+ "version": "1.17.17",
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz",
+ "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3139,12 +3137,12 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "24.3.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
- "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
+ "version": "24.9.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz",
+ "integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==",
"license": "MIT",
"dependencies": {
- "undici-types": "~7.10.0"
+ "undici-types": "~7.16.0"
}
},
"node_modules/@types/node-forge": {
@@ -3206,13 +3204,12 @@
"license": "MIT"
},
"node_modules/@types/send": {
- "version": "0.17.5",
- "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
- "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@types/mime": "^1",
"@types/node": "*"
}
},
@@ -3227,15 +3224,14 @@
}
},
"node_modules/@types/serve-static": {
- "version": "1.15.8",
- "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz",
- "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
+ "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/http-errors": "*",
- "@types/node": "*",
- "@types/send": "*"
+ "@types/node": "*"
}
},
"node_modules/@types/sockjs": {
@@ -3352,9 +3348,9 @@
}
},
"node_modules/@types/yargs": {
- "version": "16.0.9",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz",
- "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.10.tgz",
+ "integrity": "sha512-0xbOE6Ht/oj0MTVVXCCdEZzUk7adwW3YB1Tg1ZBm95jrkrUMI0VA4sf3SgxC1TG8p5aKkn3jxT9A2BDw1mM/TQ==",
"license": "MIT",
"dependencies": {
"@types/yargs-parser": "*"
@@ -3426,60 +3422,6 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@vue/babel-plugin-resolve-type/node_modules/@vue/compiler-core": {
- "version": "3.5.20",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.20.tgz",
- "integrity": "sha512-8TWXUyiqFd3GmP4JTX9hbiTFRwYHgVL/vr3cqhr4YQ258+9FADwvj7golk2sWNGHR67QgmCZ8gz80nQcMokhwg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.28.3",
- "@vue/shared": "3.5.20",
- "entities": "^4.5.0",
- "estree-walker": "^2.0.2",
- "source-map-js": "^1.2.1"
- }
- },
- "node_modules/@vue/babel-plugin-resolve-type/node_modules/@vue/compiler-dom": {
- "version": "3.5.20",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.20.tgz",
- "integrity": "sha512-whB44M59XKjqUEYOMPYU0ijUV0G+4fdrHVKDe32abNdX/kJe1NUEMqsi4cwzXa9kyM9w5S8WqFsrfo1ogtBZGQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/compiler-core": "3.5.20",
- "@vue/shared": "3.5.20"
- }
- },
- "node_modules/@vue/babel-plugin-resolve-type/node_modules/@vue/compiler-sfc": {
- "version": "3.5.20",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.20.tgz",
- "integrity": "sha512-SFcxapQc0/feWiSBfkGsa1v4DOrnMAQSYuvDMpEaxbpH5dKbnEM5KobSNSgU+1MbHCl+9ftm7oQWxvwDB6iBfw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.28.3",
- "@vue/compiler-core": "3.5.20",
- "@vue/compiler-dom": "3.5.20",
- "@vue/compiler-ssr": "3.5.20",
- "@vue/shared": "3.5.20",
- "estree-walker": "^2.0.2",
- "magic-string": "^0.30.17",
- "postcss": "^8.5.6",
- "source-map-js": "^1.2.1"
- }
- },
- "node_modules/@vue/babel-plugin-resolve-type/node_modules/@vue/compiler-ssr": {
- "version": "3.5.20",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.20.tgz",
- "integrity": "sha512-RSl5XAMc5YFUXpDQi+UQDdVjH9FnEpLDHIALg5J0ITHxkEzJ8uQLlo7CIbjPYqmZtt6w0TsIPbo1izYXwDG7JA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/compiler-dom": "3.5.20",
- "@vue/shared": "3.5.20"
- }
- },
"node_modules/@vue/babel-plugin-transform-vue-jsx": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.4.0.tgz",
@@ -3537,9 +3479,9 @@
}
},
"node_modules/@vue/babel-preset-app/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true,
"license": "ISC",
"bin": {
@@ -3978,9 +3920,9 @@
}
},
"node_modules/@vue/cli-service/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true,
"license": "ISC",
"bin": {
@@ -4042,9 +3984,9 @@
}
},
"node_modules/@vue/cli-shared-utils/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true,
"license": "ISC",
"bin": {
@@ -4061,102 +4003,60 @@
"dev": true,
"license": "ISC"
},
- "node_modules/@vue/compat": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/compat/-/compat-3.4.29.tgz",
- "integrity": "sha512-AUHFAvHycdL23N9ntJUd28yCt0Mbcy8dFVdHv1mTJ61/AVa5iTnlBwHgFPs6DAirln/TMWTfEWHqzUtQWnjOKw==",
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.24.7",
- "estree-walker": "^2.0.2",
- "source-map-js": "^1.2.0"
- },
- "peerDependencies": {
- "vue": "3.4.29"
- }
- },
"node_modules/@vue/compiler-core": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz",
- "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==",
+ "version": "3.5.22",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz",
+ "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.23.9",
- "@vue/shared": "3.4.21",
+ "@babel/parser": "^7.28.4",
+ "@vue/shared": "3.5.22",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
- "source-map-js": "^1.0.2"
+ "source-map-js": "^1.2.1"
}
},
- "node_modules/@vue/compiler-core/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@vue/compiler-dom": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz",
- "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==",
+ "version": "3.5.22",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz",
+ "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vue/compiler-core": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/compiler-core": "3.5.22",
+ "@vue/shared": "3.5.22"
}
},
- "node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@vue/compiler-sfc": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz",
- "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==",
+ "version": "3.5.22",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz",
+ "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.23.9",
- "@vue/compiler-core": "3.4.21",
- "@vue/compiler-dom": "3.4.21",
- "@vue/compiler-ssr": "3.4.21",
- "@vue/shared": "3.4.21",
+ "@babel/parser": "^7.28.4",
+ "@vue/compiler-core": "3.5.22",
+ "@vue/compiler-dom": "3.5.22",
+ "@vue/compiler-ssr": "3.5.22",
+ "@vue/shared": "3.5.22",
"estree-walker": "^2.0.2",
- "magic-string": "^0.30.7",
- "postcss": "^8.4.35",
- "source-map-js": "^1.0.2"
+ "magic-string": "^0.30.19",
+ "postcss": "^8.5.6",
+ "source-map-js": "^1.2.1"
}
},
- "node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@vue/compiler-ssr": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz",
- "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==",
+ "version": "3.5.22",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz",
+ "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/compiler-dom": "3.5.22",
+ "@vue/shared": "3.5.22"
}
},
- "node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@vue/component-compiler-utils": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz",
@@ -4266,110 +4166,110 @@
}
},
"node_modules/@vue/reactivity": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.29.tgz",
- "integrity": "sha512-w8+KV+mb1a8ornnGQitnMdLfE0kXmteaxLdccm2XwdFxXst4q/Z7SEboCV5SqJNpZbKFeaRBBJBhW24aJyGINg==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz",
+ "integrity": "sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==",
"license": "MIT",
"dependencies": {
- "@vue/shared": "3.4.29"
+ "@vue/shared": "3.5.24"
}
},
"node_modules/@vue/reactivity/node_modules/@vue/shared": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
- "integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz",
+ "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==",
"license": "MIT"
},
"node_modules/@vue/runtime-core": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.29.tgz",
- "integrity": "sha512-s8fmX3YVR/Rk5ig0ic0NuzTNjK2M7iLuVSZyMmCzN/+Mjuqqif1JasCtEtmtoJWF32pAtUjyuT2ljNKNLeOmnQ==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.24.tgz",
+ "integrity": "sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==",
"license": "MIT",
"dependencies": {
- "@vue/reactivity": "3.4.29",
- "@vue/shared": "3.4.29"
+ "@vue/reactivity": "3.5.24",
+ "@vue/shared": "3.5.24"
}
},
"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
- "integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz",
+ "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==",
"license": "MIT"
},
"node_modules/@vue/runtime-dom": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.29.tgz",
- "integrity": "sha512-gI10atCrtOLf/2MPPMM+dpz3NGulo9ZZR9d1dWo4fYvm+xkfvRrw1ZmJ7mkWtiJVXSsdmPbcK1p5dZzOCKDN0g==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz",
+ "integrity": "sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==",
"license": "MIT",
"dependencies": {
- "@vue/reactivity": "3.4.29",
- "@vue/runtime-core": "3.4.29",
- "@vue/shared": "3.4.29",
+ "@vue/reactivity": "3.5.24",
+ "@vue/runtime-core": "3.5.24",
+ "@vue/shared": "3.5.24",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
- "integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz",
+ "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==",
"license": "MIT"
},
"node_modules/@vue/server-renderer": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.29.tgz",
- "integrity": "sha512-HMLCmPI2j/k8PVkSBysrA2RxcxC5DgBiCdj7n7H2QtR8bQQPqKAe8qoaxLcInzouBmzwJ+J0x20ygN/B5mYBng==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.24.tgz",
+ "integrity": "sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-ssr": "3.4.29",
- "@vue/shared": "3.4.29"
+ "@vue/compiler-ssr": "3.5.24",
+ "@vue/shared": "3.5.24"
},
"peerDependencies": {
- "vue": "3.4.29"
+ "vue": "3.5.24"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-core": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.29.tgz",
- "integrity": "sha512-TFKiRkKKsRCKvg/jTSSKK7mYLJEQdUiUfykbG49rubC9SfDyvT2JrzTReopWlz2MxqeLyxh9UZhvxEIBgAhtrg==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz",
+ "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.24.7",
- "@vue/shared": "3.4.29",
+ "@babel/parser": "^7.28.5",
+ "@vue/shared": "3.5.24",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
- "source-map-js": "^1.2.0"
+ "source-map-js": "^1.2.1"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-dom": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.29.tgz",
- "integrity": "sha512-A6+iZ2fKIEGnfPJejdB7b1FlJzgiD+Y/sxxKwJWg1EbJu6ZPgzaPQQ51ESGNv0CP6jm6Z7/pO6Ia8Ze6IKrX7w==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz",
+ "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-core": "3.4.29",
- "@vue/shared": "3.4.29"
+ "@vue/compiler-core": "3.5.24",
+ "@vue/shared": "3.5.24"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-ssr": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.29.tgz",
- "integrity": "sha512-rFbwCmxJ16tDp3N8XCx5xSQzjhidYjXllvEcqX/lopkoznlNPz3jyy0WGJCyhAaVQK677WWFt3YO/WUEkMMUFQ==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz",
+ "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.4.29",
- "@vue/shared": "3.4.29"
+ "@vue/compiler-dom": "3.5.24",
+ "@vue/shared": "3.5.24"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
- "integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz",
+ "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==",
"license": "MIT"
},
"node_modules/@vue/shared": {
- "version": "3.5.20",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.20.tgz",
- "integrity": "sha512-SoRGP596KU/ig6TfgkCMbXkr4YJ91n/QSdMuqeP5r3hVIYA3CPHUBCc7Skak0EAKV+5lL4KyIh61VA/pK1CIAA==",
+ "version": "3.5.22",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz",
+ "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==",
"dev": true,
"license": "MIT"
},
@@ -9808,6 +9708,15 @@
],
"license": "MIT"
},
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.8.22",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.22.tgz",
+ "integrity": "sha512-/tk9kky/d8T8CTXIQYASLyhAxR5VwL3zct1oAoVTaOUHwrmsGnfbRwNdEq+vOl2BN8i3PcDdP0o4Q+jjKQoFbQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
"node_modules/batch": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
@@ -10009,9 +9918,9 @@
"license": "ISC"
},
"node_modules/bootstrap": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz",
- "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==",
+ "version": "5.3.8",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
+ "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
"funding": [
{
"type": "github",
@@ -10024,74 +9933,34 @@
],
"license": "MIT",
"peerDependencies": {
- "jquery": "1.9.1 - 3",
- "popper.js": "^1.16.1"
+ "@popperjs/core": "^2.11.8"
}
},
- "node_modules/bootstrap-vue": {
- "version": "2.23.1",
- "resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-2.23.1.tgz",
- "integrity": "sha512-SEWkG4LzmMuWjQdSYmAQk1G/oOKm37dtNfjB5kxq0YafnL2W6qUAmeDTcIZVbPiQd2OQlIkWOMPBRGySk/zGsg==",
- "hasInstallScript": true,
+ "node_modules/bootstrap-vue-next": {
+ "version": "0.40.8",
+ "resolved": "https://registry.npmjs.org/bootstrap-vue-next/-/bootstrap-vue-next-0.40.8.tgz",
+ "integrity": "sha512-8uS/UuL7RCeDgR+FD2Tr0yyUHxPDJVL8AjugeEgrXVpch36oiO+iFtfIeZVNRRWSz56h12wT2lSFmpy4b9qsBg==",
"license": "MIT",
- "dependencies": {
- "@nuxt/opencollective": "^0.3.2",
- "bootstrap": "^4.6.1",
- "popper.js": "^1.16.1",
- "portal-vue": "^2.1.7",
- "vue-functional-data-merge": "^3.1.0"
- }
- },
- "node_modules/bootstrap-vue/node_modules/@vue/compiler-sfc": {
- "version": "2.7.16",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
- "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
- "peer": true,
- "dependencies": {
- "@babel/parser": "^7.23.5",
- "postcss": "^8.4.14",
- "source-map": "^0.6.1"
- },
- "optionalDependencies": {
- "prettier": "^1.18.2 || ^2.0.0"
- }
- },
- "node_modules/bootstrap-vue/node_modules/portal-vue": {
- "version": "2.1.7",
- "resolved": "https://registry.npmjs.org/portal-vue/-/portal-vue-2.1.7.tgz",
- "integrity": "sha512-+yCno2oB3xA7irTt0EU5Ezw22L2J51uKAacE/6hMPMoO/mx3h4rXFkkBkT4GFsMDv/vEe8TNKC3ujJJ0PTwb6g==",
- "license": "MIT",
- "peerDependencies": {
- "vue": "^2.5.18"
- }
- },
- "node_modules/bootstrap-vue/node_modules/prettier": {
- "version": "2.8.8",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
- "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "bin": {
- "prettier": "bin-prettier.js"
- },
- "engines": {
- "node": ">=10.13.0"
- },
"funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
- "node_modules/bootstrap-vue/node_modules/vue": {
- "version": "2.7.16",
- "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz",
- "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
- "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@vue/compiler-sfc": "2.7.16",
- "csstype": "^3.1.0"
+ "type": "opencollective",
+ "url": "https://opencollective.com/bootstrap-vue-next"
+ },
+ "peerDependencies": {
+ "@floating-ui/vue": "*",
+ "@vueuse/core": "*",
+ "vue": "^3.5.13",
+ "vue-router": "*"
+ },
+ "peerDependenciesMeta": {
+ "@floating-ui/vue": {
+ "optional": true
+ },
+ "@vueuse/core": {
+ "optional": true
+ },
+ "vue-router": {
+ "optional": true
+ }
}
},
"node_modules/boxen": {
@@ -10218,25 +10087,24 @@
}
},
"node_modules/browserify-sign": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz",
- "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==",
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.5.tgz",
+ "integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==",
"dev": true,
"license": "ISC",
"dependencies": {
- "bn.js": "^5.2.1",
- "browserify-rsa": "^4.1.0",
+ "bn.js": "^5.2.2",
+ "browserify-rsa": "^4.1.1",
"create-hash": "^1.2.0",
"create-hmac": "^1.1.7",
- "elliptic": "^6.5.5",
- "hash-base": "~3.0",
+ "elliptic": "^6.6.1",
"inherits": "^2.0.4",
- "parse-asn1": "^5.1.7",
+ "parse-asn1": "^5.1.9",
"readable-stream": "^2.3.8",
"safe-buffer": "^5.2.1"
},
"engines": {
- "node": ">= 0.12"
+ "node": ">= 0.10"
}
},
"node_modules/browserify-sign/node_modules/isarray": {
@@ -10297,9 +10165,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.25.3",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz",
- "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==",
+ "version": "4.27.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz",
+ "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==",
"funding": [
{
"type": "opencollective",
@@ -10316,10 +10184,11 @@
],
"license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001735",
- "electron-to-chromium": "^1.5.204",
- "node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.3"
+ "baseline-browser-mapping": "^2.8.19",
+ "caniuse-lite": "^1.0.30001751",
+ "electron-to-chromium": "^1.5.238",
+ "node-releases": "^2.0.26",
+ "update-browserslist-db": "^1.1.4"
},
"bin": {
"browserslist": "cli.js"
@@ -10728,9 +10597,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001737",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz",
- "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==",
+ "version": "1.0.30001752",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001752.tgz",
+ "integrity": "sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g==",
"funding": [
{
"type": "opencollective",
@@ -10878,14 +10747,15 @@
}
},
"node_modules/cipher-base": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz",
- "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz",
+ "integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==",
"dev": true,
"license": "MIT",
"dependencies": {
"inherits": "^2.0.4",
- "safe-buffer": "^5.2.1"
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.2"
},
"engines": {
"node": ">= 0.10"
@@ -11032,9 +10902,9 @@
}
},
"node_modules/cli-truncate/node_modules/ansi-regex": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
- "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -11070,9 +10940,9 @@
}
},
"node_modules/cli-truncate/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11261,9 +11131,9 @@
}
},
"node_modules/collect-v8-coverage": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
- "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz",
+ "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==",
"license": "MIT"
},
"node_modules/collection-visit": {
@@ -11481,9 +11351,9 @@
"license": "MIT"
},
"node_modules/compression-webpack-plugin/node_modules/schema-utils": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
- "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11637,6 +11507,7 @@
"version": "2.15.3",
"resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
"integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/console-browserify": {
@@ -11821,13 +11692,13 @@
}
},
"node_modules/core-js-compat": {
- "version": "3.45.1",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz",
- "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==",
+ "version": "3.46.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.46.0.tgz",
+ "integrity": "sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==",
"dev": true,
"license": "MIT",
"dependencies": {
- "browserslist": "^4.25.3"
+ "browserslist": "^4.26.3"
},
"funding": {
"type": "opencollective",
@@ -12059,9 +11930,9 @@
}
},
"node_modules/css-loader/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true,
"license": "ISC",
"bin": {
@@ -12148,9 +12019,9 @@
"license": "MIT"
},
"node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
- "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12576,9 +12447,9 @@
"license": "MIT"
},
"node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -13437,9 +13308,9 @@
}
},
"node_modules/editorconfig/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true,
"license": "ISC",
"bin": {
@@ -13457,9 +13328,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.211",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.211.tgz",
- "integrity": "sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==",
+ "version": "1.5.244",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.244.tgz",
+ "integrity": "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==",
"license": "ISC"
},
"node_modules/elliptic": {
@@ -13583,9 +13454,9 @@
}
},
"node_modules/envinfo": {
- "version": "7.14.0",
- "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz",
- "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==",
+ "version": "7.19.0",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.19.0.tgz",
+ "integrity": "sha512-DoSM9VyG6O3vqBf+p3Gjgr/Q52HYBBtO3v+4koAxt1MnWr+zEnxE+nke/yXS4lt2P4SYCHQ4V3f1i88LQVOpAw==",
"license": "MIT",
"bin": {
"envinfo": "dist/cli.js"
@@ -13608,9 +13479,9 @@
}
},
"node_modules/error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.2.1"
@@ -14227,9 +14098,9 @@
}
},
"node_modules/eslint-plugin-vue/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true,
"license": "ISC",
"bin": {
@@ -14373,9 +14244,9 @@
"license": "MIT"
},
"node_modules/eslint-webpack-plugin/node_modules/schema-utils": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
- "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -15629,6 +15500,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -17363,14 +17244,15 @@
}
},
"node_modules/is-generator-function": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
- "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bound": "^1.0.3",
- "get-proto": "^1.0.0",
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
"has-tostringtag": "^1.0.2",
"safe-regex-test": "^1.1.0"
},
@@ -17875,9 +17757,9 @@
}
},
"node_modules/istanbul-lib-report/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -18777,9 +18659,9 @@
}
},
"node_modules/jest-snapshot/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -18958,9 +18840,9 @@
}
},
"node_modules/jest-watch-typeahead/node_modules/@types/yargs": {
- "version": "17.0.33",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
- "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==",
+ "version": "17.0.34",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz",
+ "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -19180,9 +19062,9 @@
}
},
"node_modules/jest-watch-typeahead/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -19196,9 +19078,9 @@
}
},
"node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
- "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -19295,13 +19177,6 @@
"@sideway/pinpoint": "^2.0.0"
}
},
- "node_modules/jquery": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
- "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
- "license": "MIT",
- "peer": true
- },
"node_modules/js-beautify": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz",
@@ -19696,9 +19571,9 @@
}
},
"node_modules/launch-editor": {
- "version": "2.11.1",
- "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.1.tgz",
- "integrity": "sha512-SEET7oNfgSaB6Ym0jufAdCeo3meJVeCaaDyzRygy0xsp2BFKCprcfHljTq4QkzTLUxEKkFK6OK4811YM2oSrRg==",
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.12.0.tgz",
+ "integrity": "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -19707,13 +19582,13 @@
}
},
"node_modules/launch-editor-middleware": {
- "version": "2.11.1",
- "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.11.1.tgz",
- "integrity": "sha512-6xpn4pJz5mDg2kUH7L6gK5BuZcZPdVwoSs/DhfebefwLyszNXqFFjksGup/w4CTRzzrr8FSEufDzb/gKFLle6w==",
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.12.0.tgz",
+ "integrity": "sha512-SgU5QWoR+Grq1sQedvS/RlfoyO6bdvrztpP+2RRg8UzE7Jz2Yup5J4jiFfm2J9dYBCQYD26AbJVbjnvgwdL6Pw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "launch-editor": "^2.11.1"
+ "launch-editor": "^2.12.0"
}
},
"node_modules/leven": {
@@ -20052,12 +19927,16 @@
"license": "MIT"
},
"node_modules/loader-runner": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
- "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz",
+ "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
"license": "MIT",
"engines": {
"node": ">=6.11.5"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
}
},
"node_modules/loader-utils": {
@@ -20331,9 +20210,9 @@
}
},
"node_modules/magic-string": {
- "version": "0.30.18",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
- "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
@@ -20806,9 +20685,9 @@
"license": "MIT"
},
"node_modules/mini-css-extract-plugin/node_modules/schema-utils": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
- "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -20903,6 +20782,12 @@
"node": ">=4.0.0"
}
},
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "license": "MIT"
+ },
"node_modules/mixin-deep": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
@@ -21198,6 +21083,7 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
@@ -21218,18 +21104,21 @@
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/node-fetch/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "dev": true,
"license": "BSD-2-Clause"
},
"node_modules/node-fetch/node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
@@ -21354,9 +21243,9 @@
}
},
"node_modules/node-releases": {
- "version": "2.0.19",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
- "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"license": "MIT"
},
"node_modules/nopt": {
@@ -21481,9 +21370,9 @@
"license": "MIT"
},
"node_modules/nwsapi": {
- "version": "2.2.21",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz",
- "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==",
+ "version": "2.2.22",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz",
+ "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==",
"license": "MIT"
},
"node_modules/oauth-sign": {
@@ -22054,17 +21943,16 @@
}
},
"node_modules/parse-asn1": {
- "version": "5.1.7",
- "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz",
- "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==",
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.9.tgz",
+ "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==",
"dev": true,
"license": "ISC",
"dependencies": {
"asn1.js": "^4.10.1",
"browserify-aes": "^1.2.0",
"evp_bytestokey": "^1.0.3",
- "hash-base": "~3.0",
- "pbkdf2": "^3.1.2",
+ "pbkdf2": "^3.1.5",
"safe-buffer": "^5.2.1"
},
"engines": {
@@ -22250,55 +22138,21 @@
}
},
"node_modules/pbkdf2": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.3.tgz",
- "integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz",
+ "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "create-hash": "~1.1.3",
+ "create-hash": "^1.2.0",
"create-hmac": "^1.1.7",
- "ripemd160": "=2.0.1",
+ "ripemd160": "^2.0.3",
"safe-buffer": "^5.2.1",
- "sha.js": "^2.4.11",
- "to-buffer": "^1.2.0"
+ "sha.js": "^2.4.12",
+ "to-buffer": "^1.2.1"
},
"engines": {
- "node": ">=0.12"
- }
- },
- "node_modules/pbkdf2/node_modules/create-hash": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
- "integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cipher-base": "^1.0.1",
- "inherits": "^2.0.1",
- "ripemd160": "^2.0.0",
- "sha.js": "^2.4.0"
- }
- },
- "node_modules/pbkdf2/node_modules/hash-base": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
- "integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.1"
- }
- },
- "node_modules/pbkdf2/node_modules/ripemd160": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz",
- "integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "hash-base": "^2.0.0",
- "inherits": "^2.0.1"
+ "node": ">= 0.10"
}
},
"node_modules/performance-now": {
@@ -22393,21 +22247,10 @@
"node": ">=8"
}
},
- "node_modules/popper.js": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
- "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
- "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
- "license": "MIT",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/popperjs"
- }
- },
"node_modules/portfinder": {
- "version": "1.0.37",
- "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.37.tgz",
- "integrity": "sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==",
+ "version": "1.0.38",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz",
+ "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -22673,9 +22516,9 @@
}
},
"node_modules/postcss-loader/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true,
"license": "ISC",
"bin": {
@@ -23985,9 +23828,9 @@
"license": "MIT"
},
"node_modules/regenerate-unicode-properties": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz",
- "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==",
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
+ "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -24073,18 +23916,18 @@
}
},
"node_modules/regexpu-core": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz",
- "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==",
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz",
+ "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==",
"dev": true,
"license": "MIT",
"dependencies": {
"regenerate": "^1.4.2",
- "regenerate-unicode-properties": "^10.2.0",
+ "regenerate-unicode-properties": "^10.2.2",
"regjsgen": "^0.8.0",
- "regjsparser": "^0.12.0",
+ "regjsparser": "^0.13.0",
"unicode-match-property-ecmascript": "^2.0.0",
- "unicode-match-property-value-ecmascript": "^2.1.0"
+ "unicode-match-property-value-ecmascript": "^2.2.1"
},
"engines": {
"node": ">=4"
@@ -24124,31 +23967,18 @@
"license": "MIT"
},
"node_modules/regjsparser": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz",
- "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==",
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz",
+ "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "jsesc": "~3.0.2"
+ "jsesc": "~3.1.0"
},
"bin": {
"regjsparser": "bin/parser"
}
},
- "node_modules/regjsparser/node_modules/jsesc": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
- "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
@@ -24294,12 +24124,12 @@
"license": "MIT"
},
"node_modules/resolve": {
- "version": "1.22.10",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
- "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
"license": "MIT",
"dependencies": {
- "is-core-module": "^2.16.0",
+ "is-core-module": "^2.16.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
@@ -24444,16 +24274,82 @@
}
},
"node_modules/ripemd160": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
- "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz",
+ "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "hash-base": "^3.0.0",
- "inherits": "^2.0.1"
+ "hash-base": "^3.1.2",
+ "inherits": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
+ "node_modules/ripemd160/node_modules/hash-base": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz",
+ "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^2.3.8",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/ripemd160/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ripemd160/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/ripemd160/node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ripemd160/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/ripemd160/node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -25161,9 +25057,9 @@
"license": "ISC"
},
"node_modules/simple-swizzle": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
- "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
+ "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -25171,9 +25067,9 @@
}
},
"node_modules/simple-swizzle/node_modules/is-arrayish": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
- "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
+ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
"dev": true,
"license": "MIT"
},
@@ -25225,9 +25121,9 @@
}
},
"node_modules/slice-ansi/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -26291,9 +26187,9 @@
}
},
"node_modules/tapable": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz",
- "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -26333,13 +26229,13 @@
}
},
"node_modules/terser": {
- "version": "5.43.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
- "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
+ "version": "5.44.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz",
+ "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==",
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.14.0",
+ "acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
@@ -26419,9 +26315,9 @@
"license": "MIT"
},
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
- "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
@@ -26650,9 +26546,9 @@
"license": "MIT"
},
"node_modules/to-buffer": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz",
- "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
+ "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -27105,9 +27001,9 @@
}
},
"node_modules/undici-types": {
- "version": "7.10.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
- "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"license": "MIT"
},
"node_modules/unicode-canonical-property-names-ecmascript": {
@@ -27135,9 +27031,9 @@
}
},
"node_modules/unicode-match-property-value-ecmascript": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz",
- "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
+ "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -27145,9 +27041,9 @@
}
},
"node_modules/unicode-property-aliases-ecmascript": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
- "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
+ "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -27315,9 +27211,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
- "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
+ "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
"funding": [
{
"type": "opencollective",
@@ -27626,16 +27522,16 @@
"license": "MIT"
},
"node_modules/vue": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.29.tgz",
- "integrity": "sha512-8QUYfRcYzNlYuzKPfge1UWC6nF9ym0lx7mpGVPJYNhddxEf3DD0+kU07NTL0sXuiT2HuJuKr/iEO8WvXvT0RSQ==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz",
+ "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.4.29",
- "@vue/compiler-sfc": "3.4.29",
- "@vue/runtime-dom": "3.4.29",
- "@vue/server-renderer": "3.4.29",
- "@vue/shared": "3.4.29"
+ "@vue/compiler-dom": "3.5.24",
+ "@vue/compiler-sfc": "3.5.24",
+ "@vue/runtime-dom": "3.5.24",
+ "@vue/server-renderer": "3.5.24",
+ "@vue/shared": "3.5.24"
},
"peerDependencies": {
"typescript": "*"
@@ -27653,6 +27549,32 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
"node_modules/vue-eslint-parser": {
"version": "9.4.3",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz",
@@ -27679,9 +27601,9 @@
}
},
"node_modules/vue-eslint-parser/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true,
"license": "ISC",
"bin": {
@@ -27691,12 +27613,6 @@
"node": ">=10"
}
},
- "node_modules/vue-functional-data-merge": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz",
- "integrity": "sha512-leT4kdJVQyeZNY1kmnS1xiUlQ9z1B/kdBFCILIjYYQDqZgLqCLa0UhjSSeRX6c3mUe6U5qYeM8LrEqkHJ1B4LA==",
- "license": "MIT"
- },
"node_modules/vue-hot-reload-api": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
@@ -27705,13 +27621,13 @@
"license": "MIT"
},
"node_modules/vue-i18n": {
- "version": "10.0.5",
- "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.5.tgz",
- "integrity": "sha512-9/gmDlCblz3i8ypu/afiIc/SUIfTTE1mr0mZhb9pk70xo2csHAM9mp2gdQ3KD2O0AM3Hz/5ypb+FycTj/lHlPQ==",
+ "version": "10.0.8",
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.8.tgz",
+ "integrity": "sha512-mIjy4utxMz9lMMo6G9vYePv7gUFt4ztOMhY9/4czDJxZ26xPeJ49MAGa9wBAE3XuXbYCrtVPmPxNjej7JJJkZQ==",
"license": "MIT",
"dependencies": {
- "@intlify/core-base": "10.0.5",
- "@intlify/shared": "10.0.5",
+ "@intlify/core-base": "10.0.8",
+ "@intlify/shared": "10.0.8",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
@@ -27763,18 +27679,18 @@
}
},
"node_modules/vue-router": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.0.tgz",
- "integrity": "sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==",
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz",
+ "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==",
"license": "MIT",
"dependencies": {
- "@vue/devtools-api": "^6.5.1"
+ "@vue/devtools-api": "^6.6.4"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
- "vue": "^3.2.0"
+ "vue": "^3.5.0"
}
},
"node_modules/vue-server-renderer": {
@@ -28120,59 +28036,59 @@
"license": "MIT"
},
"node_modules/vue/node_modules/@vue/compiler-core": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.29.tgz",
- "integrity": "sha512-TFKiRkKKsRCKvg/jTSSKK7mYLJEQdUiUfykbG49rubC9SfDyvT2JrzTReopWlz2MxqeLyxh9UZhvxEIBgAhtrg==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz",
+ "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.24.7",
- "@vue/shared": "3.4.29",
+ "@babel/parser": "^7.28.5",
+ "@vue/shared": "3.5.24",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
- "source-map-js": "^1.2.0"
+ "source-map-js": "^1.2.1"
}
},
"node_modules/vue/node_modules/@vue/compiler-dom": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.29.tgz",
- "integrity": "sha512-A6+iZ2fKIEGnfPJejdB7b1FlJzgiD+Y/sxxKwJWg1EbJu6ZPgzaPQQ51ESGNv0CP6jm6Z7/pO6Ia8Ze6IKrX7w==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz",
+ "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-core": "3.4.29",
- "@vue/shared": "3.4.29"
+ "@vue/compiler-core": "3.5.24",
+ "@vue/shared": "3.5.24"
}
},
"node_modules/vue/node_modules/@vue/compiler-sfc": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.29.tgz",
- "integrity": "sha512-zygDcEtn8ZimDlrEQyLUovoWgKQic6aEQqRXce2WXBvSeHbEbcAsXyCk9oG33ZkyWH4sl9D3tkYc1idoOkdqZQ==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz",
+ "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.24.7",
- "@vue/compiler-core": "3.4.29",
- "@vue/compiler-dom": "3.4.29",
- "@vue/compiler-ssr": "3.4.29",
- "@vue/shared": "3.4.29",
+ "@babel/parser": "^7.28.5",
+ "@vue/compiler-core": "3.5.24",
+ "@vue/compiler-dom": "3.5.24",
+ "@vue/compiler-ssr": "3.5.24",
+ "@vue/shared": "3.5.24",
"estree-walker": "^2.0.2",
- "magic-string": "^0.30.10",
- "postcss": "^8.4.38",
- "source-map-js": "^1.2.0"
+ "magic-string": "^0.30.21",
+ "postcss": "^8.5.6",
+ "source-map-js": "^1.2.1"
}
},
"node_modules/vue/node_modules/@vue/compiler-ssr": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.29.tgz",
- "integrity": "sha512-rFbwCmxJ16tDp3N8XCx5xSQzjhidYjXllvEcqX/lopkoznlNPz3jyy0WGJCyhAaVQK677WWFt3YO/WUEkMMUFQ==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz",
+ "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.4.29",
- "@vue/shared": "3.4.29"
+ "@vue/compiler-dom": "3.5.24",
+ "@vue/shared": "3.5.24"
}
},
"node_modules/vue/node_modules/@vue/shared": {
- "version": "3.4.29",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
- "integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz",
+ "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==",
"license": "MIT"
},
"node_modules/vuelidate": {
@@ -28678,9 +28594,9 @@
}
},
"node_modules/webpack": {
- "version": "5.101.3",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz",
- "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==",
+ "version": "5.102.1",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz",
+ "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==",
"license": "MIT",
"dependencies": {
"@types/eslint-scope": "^3.7.7",
@@ -28691,7 +28607,7 @@
"@webassemblyjs/wasm-parser": "^1.14.1",
"acorn": "^8.15.0",
"acorn-import-phases": "^1.0.3",
- "browserslist": "^4.24.0",
+ "browserslist": "^4.26.3",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.17.3",
"es-module-lexer": "^1.2.1",
@@ -28703,10 +28619,10 @@
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
- "schema-utils": "^4.3.2",
- "tapable": "^2.1.1",
+ "schema-utils": "^4.3.3",
+ "tapable": "^2.3.0",
"terser-webpack-plugin": "^5.3.11",
- "watchpack": "^2.4.1",
+ "watchpack": "^2.4.4",
"webpack-sources": "^3.3.3"
},
"bin": {
@@ -28916,9 +28832,9 @@
"license": "MIT"
},
"node_modules/webpack-dev-middleware/node_modules/schema-utils": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
- "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -28996,22 +28912,22 @@
}
},
"node_modules/webpack-dev-server/node_modules/@types/express": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz",
- "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==",
+ "version": "4.17.25",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz",
+ "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
- "@types/serve-static": "*"
+ "@types/serve-static": "^1"
}
},
"node_modules/webpack-dev-server/node_modules/@types/express-serve-static-core": {
- "version": "4.19.6",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz",
- "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==",
+ "version": "4.19.7",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz",
+ "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -29021,6 +28937,29 @@
"@types/send": "*"
}
},
+ "node_modules/webpack-dev-server/node_modules/@types/send": {
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz",
+ "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/webpack-dev-server/node_modules/@types/serve-static": {
+ "version": "1.15.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz",
+ "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-errors": "*",
+ "@types/node": "*",
+ "@types/send": "<1"
+ }
+ },
"node_modules/webpack-dev-server/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
@@ -29101,9 +29040,9 @@
"license": "MIT"
},
"node_modules/webpack-dev-server/node_modules/schema-utils": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
- "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -29252,9 +29191,9 @@
"license": "MIT"
},
"node_modules/webpack/node_modules/schema-utils": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
- "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
diff --git a/package.json b/package.json
index cae7b7a..d5bdb95 100644
--- a/package.json
+++ b/package.json
@@ -17,23 +17,24 @@
"dependencies": {
"@carbon/icons-vue": "10.49.1",
"@novnc/novnc": "1.3.0",
- "@vue/compat": "3.4.29",
"@vue/vue3-jest": "27.0.0",
"@vuelidate/core": "2.0.3",
"@vuelidate/validators": "2.0.4",
"axios": "1.6.0",
"axios-cache-interceptor": "1.5.1",
- "bootstrap": "4.6.2",
- "bootstrap-vue": "2.23.1",
+ "bootstrap": "5.3.8",
+ "bootstrap-vue-next": "0.40.8",
"core-js": "3.23.4",
"date-fns": "2.28.0",
"date-fns-tz": "1.3.6",
"js-cookie": "3.0.1",
"lodash": "4.17.21",
- "vue": "3.4.29",
- "vue-i18n": "10.0.5",
+ "mitt": "3.0.1",
+ "vue": "3.5.24",
+ "vue-demi": "0.14.10",
+ "vue-i18n": "10.0.8",
"vue-loader": "17.4.2",
- "vue-router": "4.4.0",
+ "vue-router": "4.6.3",
"vuelidate": "0.7.7",
"vuex": "4.1.0",
"webpack-cli": "5.1.4",
@@ -48,7 +49,7 @@
"@vue/cli-plugin-unit-jest": "5.0.8",
"@vue/cli-plugin-vuex": "5.0.8",
"@vue/cli-service": "5.0.8",
- "@vue/compiler-sfc": "3.4.21",
+ "@vue/compiler-sfc": "3.5.22",
"@vue/eslint-config-prettier": "9.0.0",
"@vue/test-utils": "2.4.5",
"autoprefixer": "9.8.6",
diff --git a/public/index.html b/public/index.html
index e96efda..c355ece 100644
--- a/public/index.html
+++ b/public/index.html
@@ -5,7 +5,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
- <link rel="icon" href="<%= require('./favicon.ico') %>">
+ <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>OpenBMC Web UI</title>
</head>
<body>
diff --git a/src/App.vue b/src/App.vue
index 3a56c7c..5878718 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,12 +1,18 @@
<template>
<div id="app">
<router-view />
+ <confirm-modal />
+ <b-orchestrator />
</div>
</template>
<script>
+import ConfirmModal from '@/components/Global/ConfirmModal.vue';
+import { BOrchestrator } from 'bootstrap-vue-next';
+
export default {
name: 'App',
+ components: { ConfirmModal, BOrchestrator },
computed: {
assetTag() {
return '';
diff --git a/src/assets/styles/_obmc-custom.scss b/src/assets/styles/_obmc-custom.scss
index 068364c..645d2b3 100644
--- a/src/assets/styles/_obmc-custom.scss
+++ b/src/assets/styles/_obmc-custom.scss
@@ -1,6 +1,5 @@
// Vendor styles
@import "./bootstrap";
-@import "~bootstrap-vue/src/index";
// Custom BMC styles
@import "./bmc/custom";
diff --git a/src/assets/styles/bmc/custom/_alert.scss b/src/assets/styles/bmc/custom/_alert.scss
index 0e78ba6..c7911fe 100644
--- a/src/assets/styles/bmc/custom/_alert.scss
+++ b/src/assets/styles/bmc/custom/_alert.scss
@@ -1,12 +1,13 @@
+
.alert {
display: flex;
padding: $spacer;
- border-width: 0 0 0 3px;
- color: gray("800");
+ border-width: 0 0 0 3px; // keep physical width for browsers; color uses logical start
+ color: $gray-800;
margin-bottom: $spacer;
&.small {
- padding: $spacer / 2;
+ padding: calc($spacer / 2);
font-size: 1rem;
}
@@ -14,56 +15,66 @@
font-weight: 300;
opacity: 1;
}
+}
- .alert-icon {
- display: inline-flex;
- align-items: flex-start;
- margin-right: $spacer;
- margin-bottom: $spacer;
+// Bootstrap 5 alert structure has deeper nesting - use descendant selectors
+.alert .alert-body{
+ display: inline-flex;
+ flex-direction: row;
+}
- @include media-breakpoint-up(sm) {
- margin-bottom: 0;
- }
- }
+.alert .alert-icon {
+ display: inline-flex;
+ align-items: flex-start;
+ margin-inline-end: $spacer;
+ margin-bottom: $spacer;
- .alert-content {
- flex: 1 1 auto;
- }
+ @include media-breakpoint-up(sm) {
+ margin-bottom: 0;
+ }
+}
- .alert-title {
- margin-bottom: $spacer / 2;
- }
+.alert .alert-content {
+ flex: 1 1 auto;
+}
- .alert-msg {
- p + p {
- margin-bottom: $spacer;
- }
+.alert .alert-title {
+ margin-bottom: calc($spacer / 2);
+}
- p:last-of-type {
- margin-bottom: 0;
- }
- }
+.alert .alert-msg {
+ p + p {
+ margin-bottom: $spacer;
+ }
+
+ p:last-of-type {
+ margin-bottom: 0;
+ }
+}
+
+// Alert variant styles
+.alert {
&.alert-info {
- border-left-color: theme-color("info");
+ border-inline-start-color: theme-color("info");
background-color: theme-color-light("info");
fill: theme-color("info");
}
&.alert-success {
- border-left-color: theme-color("success");
+ border-inline-start-color: theme-color("success");
background-color: theme-color-light("success");
fill: theme-color("success");
}
&.alert-danger {
- border-left-color: theme-color("danger");
+ border-inline-start-color: theme-color("danger");
background-color: theme-color-light("danger");
fill: theme-color("danger");
}
&.alert-warning {
- border-left-color: theme-color("warning");
+ border-inline-start-color: theme-color("warning");
background-color: theme-color-light("warning");
fill: theme-color("warning");
}
diff --git a/src/assets/styles/bmc/custom/_badge.scss b/src/assets/styles/bmc/custom/_badge.scss
index 0b88b49..01d05a8 100644
--- a/src/assets/styles/bmc/custom/_badge.scss
+++ b/src/assets/styles/bmc/custom/_badge.scss
@@ -1,3 +1,4 @@
+
.badge-pill {
// Need to explicitly set border-radius
// for pill variant because global $enable-rounded
@@ -9,7 +10,7 @@
display: inline-flex;
.close {
font-size: 1em;
- margin-left: $spacer/2;
+ margin-inline-start: calc($spacer / 2);
font-weight: inherit;
color: inherit;
}
diff --git a/src/assets/styles/bmc/custom/_base.scss b/src/assets/styles/bmc/custom/_base.scss
index c11e046..61f5813 100644
--- a/src/assets/styles/bmc/custom/_base.scss
+++ b/src/assets/styles/bmc/custom/_base.scss
@@ -1,7 +1,7 @@
dt,
legend,
label {
- color: gray("800");
+ color: $gray-800;
font-size: 14px;
font-weight: 400;
line-height: 1.4285;
diff --git a/src/assets/styles/bmc/custom/_bootstrap-grid.scss b/src/assets/styles/bmc/custom/_bootstrap-grid.scss
index 7ad7c81..c2cf8b6 100644
--- a/src/assets/styles/bmc/custom/_bootstrap-grid.scss
+++ b/src/assets/styles/bmc/custom/_bootstrap-grid.scss
@@ -4,5 +4,5 @@
// is set, setting the left margin to 0 is needed
// so the content doesn't center align
// https://bootstrap-vue.org/docs/components/layout#fluid-width-container
- margin-left: 0;
+ margin-inline-start: 0;
}
\ No newline at end of file
diff --git a/src/assets/styles/bmc/custom/_buttons.scss b/src/assets/styles/bmc/custom/_buttons.scss
index 0ab7baa..008f08d 100644
--- a/src/assets/styles/bmc/custom/_buttons.scss
+++ b/src/assets/styles/bmc/custom/_buttons.scss
@@ -1,23 +1,23 @@
@import 'bootstrap/dist/css/bootstrap.css';
.btn {
- padding-top: $spacer / 2;
- padding-right: $spacer;
- padding-bottom: $spacer / 2;
- padding-left: $spacer;
+ padding-top: calc($spacer / 2);
+ padding-inline-end: $spacer;
+ padding-bottom: calc($spacer / 2);
+ padding-inline-start: $spacer;
display: inline-flex;
align-items: center;
justify-content: space-around;
svg {
- margin-right: $spacer / 4;
+ margin-inline-end: calc($spacer / 4);
}
&:disabled {
- color: gray("600");
+ color: $gray-600;
fill: currentColor;
box-shadow: none !important;
&:not(.btn-link) {
- border-color: gray("400");
- background-color: gray("400");
+ border-color: $gray-400;
+ background-color: $gray-400;
}
}
}
@@ -46,11 +46,11 @@
fill: theme-color("primary");
text-decoration: none !important;
&:hover {
- background-color: gray("200");
+ background-color: $gray-200;
color: theme-color("primary");
}
&:active {
- background-color: gray("300");
+ background-color: $gray-300;
}
&:focus {
box-shadow: inset 0 0 0 2px theme-color("primary");
@@ -64,14 +64,14 @@
// Icon only buttons
.btn-icon-only svg {
- margin-right: 0;
+ margin-inline-end: 0;
}
// Datepicker, clear search and Password toggle buttons
.input-action-btn,
.btn-datepicker {
position: absolute;
- right: 0;
+ inset-inline-end: 0;
top: 0;
z-index: $zindex-dropdown + 1;
}
diff --git a/src/assets/styles/bmc/custom/_dropdown.scss b/src/assets/styles/bmc/custom/_dropdown.scss
index 82dda86..5f90fac 100644
--- a/src/assets/styles/bmc/custom/_dropdown.scss
+++ b/src/assets/styles/bmc/custom/_dropdown.scss
@@ -1,26 +1,28 @@
+
// Make calendar visible over the table
.dropdown-menu {
z-index: $zindex-dropdown + 1;
padding: 0;
}
.dropdown-item {
- padding-left: $spacer/4;
- margin-top: -1 * $spacer/4;
+ padding-inline-start: calc(#{$spacer} / 4);
+ margin-top: calc(-1 * (#{$spacer} / 4));
}
.b-dropdown-form {
- padding: $spacer/2;
+ padding: calc(#{$spacer} / 2);
+ // Maintain .form-group spacing for backward compatibility
.form-group {
- margin-bottom: $spacer/2;
+ margin-bottom: calc(#{$spacer} / 2);
}
}
// Table filter dropdown clear button style
.table-filter {
.dropdown-item {
&:hover {
- background-color: gray("200");
+ background-color: $gray-200;
}
&:active {
- background-color: gray("300");
+ background-color: $gray-300;
}
&:focus {
outline: none;
diff --git a/src/assets/styles/bmc/custom/_forms.scss b/src/assets/styles/bmc/custom/_forms.scss
index 428a40c..8c262a6 100644
--- a/src/assets/styles/bmc/custom/_forms.scss
+++ b/src/assets/styles/bmc/custom/_forms.scss
@@ -1,46 +1,53 @@
-// Helper text
-.form-text {
+
+// Helper text (Bootstrap 4 and bootstrap-vue-next)
+.form-text,
+.b-form-text {
+ display: block;
font-size: $form-label-font-size;
line-height: $form-line-height;
- margin-top: -$spacer / 4;
- margin-bottom: $spacer / 2;
- color: gray("700")!important;
+ margin-top: calc(-1 * (#{$spacer} / 4));
+ margin-bottom: calc(#{$spacer} / 2);
+ color: $gray-700!important;
}
// Legend label
.col-form-label {
- color: gray("800");
+ color: $gray-800;
font-size: $form-label-font-size;
line-height: $form-line-height;
}
-.form-group {
+// Bootstrap 4 and bootstrap-vue-next form group spacing
+.form-group,
+.b-form-group {
margin-bottom: $spacer * 2;
}
.custom-select,
+.form-select,
.form-control,
.input-group-text {
- border-color: gray("500") !important;
- background-color: gray("100");
+ border-color: $gray-500 !important;
+ background-color: $gray-100;
}
.custom-select,
+.form-select,
.form-control {
&:active {
border: 1px solid $primary!important;
}
&:focus {
color: theme-color("dark");
- background-color: gray("100");
- box-shadow: inset 0 0 0 3px gray("100"), inset 0 0 0 5px $primary !important;
+ background-color: $gray-100;
+ box-shadow: inset 0 0 0 3px $gray-100, inset 0 0 0 5px $primary !important;
}
&:disabled {
- background-color: gray("400");
- color: gray("600");
+ background-color: $gray-400;
+ color: $gray-600;
}
&::placeholder {
- color: gray("600");
+ color: $gray-600;
}
&.is-invalid,
&:invalid {
@@ -49,7 +56,9 @@
}
.custom-select,
+.form-select,
.custom-control-label,
+.form-check-label,
.form-control {
color: theme-color("dark") !important;
font-size: 1rem;
@@ -57,16 +66,17 @@
// Inverted form colors
.form-background {
- background-color: gray("100");
+ background-color: $gray-100;
.custom-select,
+ .form-select,
.form-control {
background-color: $white;
&:focus {
background-color: $white;
}
&:disabled {
- background-color: gray("400");
- color: gray("600");
+ background-color: $gray-400;
+ color: $gray-600;
}
}
}
@@ -76,9 +86,12 @@
line-height: $form-line-height;
}
+// Checkbox and radio button styling with backward compatibility
.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after,
.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before,
-.custom-control-input:checked ~ .custom-control-label::before {
+.custom-control-input:checked ~ .custom-control-label::before,
+.form-check-input:checked,
+.form-check-input:indeterminate {
background-color: $black;
border-color: $black;
cursor: pointer;
@@ -89,7 +102,17 @@
& + .custom-control-label {
// Disabled label for checkbox, radio,
// switch bootstrap form components
- color: gray("600")!important;
+ color: $gray-600!important;
+ }
+ }
+}
+
+.form-check {
+ .form-check-input[disabled=disabled] {
+ & + .form-check-label {
+ // Disabled label for checkbox, radio,
+ // switch bootstrap form components
+ color: $gray-600!important;
}
}
}
@@ -98,7 +121,12 @@
box-shadow: 0 0 0 2px theme-color("primary");
}
-.custom-control-label::after {
+.form-check-input:focus {
+ box-shadow: 0 0 0 2px theme-color("primary");
+}
+
+.custom-control-label::after,
+.form-check-label {
cursor: pointer;
}
@@ -110,7 +138,7 @@
.b-form-tags-button {
// Add button inside input field
white-space: nowrap;
- margin-right: -$spacer;
+ margin-inline-end: -$spacer;
&.btn-link-primary {
color: theme-color("primary");
fill: currentColor;
@@ -130,3 +158,8 @@
background-position: right 3rem bottom 50%;
}
}
+
+// Bootstrap 5 form-switch styling for curved toggle switches
+.form-switch .form-check-input {
+ border-radius: 1.5625rem; // 2em equivalent to match Bootstrap 5's $form-switch-border-radius
+}
diff --git a/src/assets/styles/bmc/custom/_modal.scss b/src/assets/styles/bmc/custom/_modal.scss
index e2fa0cd..d55e8b4 100644
--- a/src/assets/styles/bmc/custom/_modal.scss
+++ b/src/assets/styles/bmc/custom/_modal.scss
@@ -1,7 +1,6 @@
.modal-header {
- .close {
+ .btn-close {
font-weight: normal;
- color: theme-color("dark");
opacity: 1;
}
.modal-title {
diff --git a/src/assets/styles/bmc/custom/_pagination.scss b/src/assets/styles/bmc/custom/_pagination.scss
index d38ce5d..662af0f 100644
--- a/src/assets/styles/bmc/custom/_pagination.scss
+++ b/src/assets/styles/bmc/custom/_pagination.scss
@@ -6,7 +6,7 @@
width: fit-content;
}
label {
- margin-left: $spacer;
+ margin-inline-start: $spacer;
line-height: $spacer * 2;
}
}
diff --git a/src/assets/styles/bmc/custom/_section-divider.scss b/src/assets/styles/bmc/custom/_section-divider.scss
index 620c9e5..bed2e1a 100644
--- a/src/assets/styles/bmc/custom/_section-divider.scss
+++ b/src/assets/styles/bmc/custom/_section-divider.scss
@@ -1,3 +1,3 @@
.section-divider {
- border-bottom: 1px solid gray('400');
+ border-bottom: 1px solid $gray-400;
}
\ No newline at end of file
diff --git a/src/assets/styles/bmc/custom/_tables.scss b/src/assets/styles/bmc/custom/_tables.scss
index e8b5a83..d6d5a74 100644
--- a/src/assets/styles/bmc/custom/_tables.scss
+++ b/src/assets/styles/bmc/custom/_tables.scss
@@ -1,39 +1,43 @@
+
.table {
position: relative;
z-index: $zindex-dropdown;
td {
- border-top: 1px solid gray("300");
- border-bottom: 1px solid gray("300");
+ border-top: 1px solid $gray-300;
+ border-bottom: 1px solid $gray-300;
&:first-of-type {
- border-left: 1px solid gray("300");
+ border-inline-start: 1px solid $gray-300;
}
&:last-of-type {
- border-right: 1px solid gray("300");
+ border-inline-end: 1px solid $gray-300;
}
vertical-align: middle;
// Table action buttons
.btn-link {
- width: 40px;
- height: 40px;
- padding: 5px !important;
+ padding: 0.25rem 0.5rem !important;
display: inline-flex;
justify-content: center;
align-items: center;
+ line-height: 1;
+ .visually-hidden {
+ margin-inline-start: 0.25rem;
+ }
}
}
- // thead-light added for specificity
- .thead-light th {
+ // Bootstrap 4 thead-light and Bootstrap 5 table-light classes for specificity
+ .thead-light th,
+ .table-light th {
vertical-align: middle;
- border-top: 1px solid gray("300");
- border-bottom: 1px solid gray("300");
+ border-top: 1px solid $gray-300;
+ border-bottom: 1px solid $gray-300;
&:first-of-type {
- border-left: 1px solid gray("300");
+ border-inline-start: 1px solid $gray-300;
}
&:last-of-type {
- border-right: 1px solid gray("300");
+ border-inline-end: 1px solid $gray-300;
}
color: theme-color("dark");
&:focus {
@@ -50,24 +54,26 @@
td {
border-bottom: none;
}
- .table-row-expand svg {
- transform: rotate(180deg);
- }
+ }
+
+ // Show right-pointing chevron when collapsed
+ .table-row-expand .btn.collapsed svg {
+ transform: rotate(-90deg);
}
.b-table-details {
background-color: theme-color("light");
td {
- padding-left: calc(50px + (#{$table-cell-padding} * 2));
- padding-right: calc(50px + (#{$table-cell-padding} * 2));
+ padding-inline-start: calc(50px + (#{$table-cell-padding-x} * 2));
+ padding-inline-end: calc(50px + (#{$table-cell-padding-x} * 2));
}
dl {
margin: 0;
}
dt {
- float: left;
- clear: left;
- margin-right: $spacer / 2;
+ float: inline-start;
+ clear: inline-start;
+ margin-inline-end: calc($spacer / 2);
}
dd {
line-height: 1.2
@@ -78,7 +84,6 @@
width: 50px;
.btn {
padding: 0;
- width: 50px;
}
svg {
fill: theme-color("dark");
@@ -86,7 +91,7 @@
}
.b-table-sort-icon-left {
background-position: left calc(1.5rem / 2) center !important;
- padding-left: calc(1.2rem + 0.65em) !important;
+ padding-inline-start: calc(1.2rem + 0.65em) !important;
&:focus {
outline: none;
box-shadow: inset 0 0 0 2px theme-color('primary') !important;
@@ -101,15 +106,15 @@
border-top: none;
}
-// Table stacked style for small screen only
-@include media-breakpoint-down(xs) {
+// Table stacked style for small screens (< 576px)
+@include media-breakpoint-down(sm) {
.b-table-stacked-sm {
- border: 1px solid gray("300");
+ border: 1px solid $gray-300;
tr {
&:not(:first-child) > td[aria-colindex='1'] {
- border-top: 1px solid gray("300");
+ border-top: 1px solid $gray-300;
padding-top: 0.625rem;
}
@@ -126,29 +131,29 @@
&:before {
content: '';
- background-color: gray("200");
+ background-color: $gray-200;
width: 40%;
- border-right: 1px solid gray("300");
+ border-inline-end: 1px solid $gray-300;
}
&:after {
content: '';
- right: 0;
+ inset-inline-end: 0;
width: 60%;
}
&:nth-child(even)::after {
- background-color: gray("100"); // Zebra striping for the row
+ background-color: $gray-100; // Zebra striping for the row
}
}
td {
border: 0;
padding: 0.75rem;
- text-align: left !important;
+ text-align: start !important;
&:last-of-type {
- border-right: 0;
+ border-inline-end: 0;
}
}
}
@@ -156,12 +161,12 @@
.table.b-table.b-table-stacked-sm > tbody > tr > [data-label] {
&::before {
- text-align: left;
- padding-left: $spacer /2;
+ text-align: start;
+ padding-inline-start: calc($spacer / 2);
}
> div {
- padding-left: 1rem;
+ padding-inline-start: 1rem;
}
}
diff --git a/src/assets/styles/bmc/custom/_toasts.scss b/src/assets/styles/bmc/custom/_toasts.scss
index 4e2ad7f..900794f 100644
--- a/src/assets/styles/bmc/custom/_toasts.scss
+++ b/src/assets/styles/bmc/custom/_toasts.scss
@@ -1,16 +1,23 @@
-.b-toaster {
- top: 75px!important; // make sure toasts do not hide top header
+// Toast container positioning - below AppHeader
+.b-toaster,
+.toast-container {
+ top: calc(#{$header-height} + #{$spacer})!important; // position below AppHeader (48px + spacing)
}
// Toast component and status icon style
.toast {
- padding: $spacer/2 $spacer/2 $spacer/2 $spacer+2;
- border-width: 0 0 0 3px;
+ padding: calc(#{$spacer} / 2) calc(#{$spacer} / 2) calc(#{$spacer} / 2) $spacer+2;
+ border-width: 0 0 0 3px; // physical width retained; color via logical property
box-shadow: $box-shadow;
+ margin-bottom: $spacer; // vertical spacing between stacked toasts
.close {
font-weight: 300;
opacity: 1;
}
+ // Hide progress bar in all toasts (CSS workaround since JS props don't work as documented in Bootstrap Vue Next 0.40.8)
+ .progress {
+ display: none !important;
+ }
}
.toast-header {
@@ -19,15 +26,15 @@
background-color: inherit!important; //override specificity
border: none;
color: theme-color("dark")!important; //override specificity
- padding-bottom: 0;
+ padding-bottom: calc(#{$spacer} / 4); // spacing between header and body
}
.toast-icon {
display: flex;
- margin-right: 1rem;
+ margin-inline-end: 1rem;
svg {
- margin-left: -2.5rem;
+ margin-inline-start: -2.5rem;
}
+ .close {
@@ -37,25 +44,77 @@
.toast-body {
color: theme-color("dark");
- padding-top: 0;
+ padding-top: calc(#{$spacer} / 4); // spacing below header
+ white-space: pre-line; // Preserve newlines from \n characters
+ line-height: 1.6; // Better line spacing for multi-line content and timestamps
}
+// Bootstrap Vue 2 class names (backward compatibility)
.b-toast-success .toast {
- border-left-color: theme-color("success")!important;
+ border-inline-start-color: theme-color("success")!important;
background-color: theme-color-light("success")!important;
}
.b-toast-info .toast {
- border-left-color: theme-color("info")!important;
+ border-inline-start-color: theme-color("info")!important;
background-color: theme-color-light("info")!important;
}
.b-toast-danger .toast {
- border-left-color: theme-color("danger")!important;
+ border-inline-start-color: theme-color("danger")!important;
background-color: theme-color-light("danger")!important;
}
.b-toast-warning .toast {
- border-left-color: theme-color("warning")!important;
+ border-inline-start-color: theme-color("warning")!important;
+ background-color: theme-color-light("warning")!important;
+}
+
+// Bootstrap Vue Next class names
+.toast.bg-success {
+ border-inline-start-color: theme-color("success")!important;
+ background-color: theme-color-light("success")!important;
+}
+
+.toast.bg-info {
+ border-inline-start-color: theme-color("info")!important;
+ background-color: theme-color-light("info")!important;
+}
+
+.toast.bg-danger {
+ border-inline-start-color: theme-color("danger")!important;
+ background-color: theme-color-light("danger")!important;
+}
+
+.toast.bg-warning {
+ border-inline-start-color: theme-color("warning")!important;
+ background-color: theme-color-light("warning")!important;
+}
+
+// Override Bootstrap's solid variant backgrounds
+.toast.text-bg-success,
+.toast.text-bg-info,
+.toast.text-bg-danger,
+.toast.text-bg-warning {
+ color: theme-color("dark")!important;
+}
+
+.toast.text-bg-success {
+ border-inline-start-color: theme-color("success")!important;
+ background-color: theme-color-light("success")!important;
+}
+
+.toast.text-bg-info {
+ border-inline-start-color: theme-color("info")!important;
+ background-color: theme-color-light("info")!important;
+}
+
+.toast.text-bg-danger {
+ border-inline-start-color: theme-color("danger")!important;
+ background-color: theme-color-light("danger")!important;
+}
+
+.toast.text-bg-warning {
+ border-inline-start-color: theme-color("warning")!important;
background-color: theme-color-light("warning")!important;
}
\ No newline at end of file
diff --git a/src/assets/styles/bmc/helpers/_colors.scss b/src/assets/styles/bmc/helpers/_colors.scss
index cffe39d..648ede7 100644
--- a/src/assets/styles/bmc/helpers/_colors.scss
+++ b/src/assets/styles/bmc/helpers/_colors.scss
@@ -34,5 +34,17 @@
$success: $green;
$warning: $yellow;
+// Bootstrap 5 requires $theme-colors map
+$theme-colors: (
+ "danger": $danger,
+ "dark": $dark,
+ "info": $info,
+ "light": $light,
+ "primary": $primary,
+ "secondary": $secondary,
+ "success": $success,
+ "warning": $warning
+);
+
$loading-color: $primary;
$navbar-color: $dark;
diff --git a/src/assets/styles/bmc/helpers/_functions.scss b/src/assets/styles/bmc/helpers/_functions.scss
index 57956c9..0d89cc4 100644
--- a/src/assets/styles/bmc/helpers/_functions.scss
+++ b/src/assets/styles/bmc/helpers/_functions.scss
@@ -1,9 +1,32 @@
+@use "sass:math";
+
// This function is usually used to get a lighter
// theme variant color to use as a background color
@function theme-color-light($variant) {
- @return theme-color-level($variant, -11.3);
+ @return shift-color(map-get($theme-colors, $variant), -90.4%);
}
@function theme-color-dark($variant) {
- @return theme-color-level($variant, 2);
+ @return shift-color(map-get($theme-colors, $variant), 16%);
+}
+
+// Bootstrap 5 no longer provides theme-color(); define a compatible helper
+@function theme-color($variant) {
+ @return map-get($theme-colors, $variant);
+}
+
+// Bootstrap 4 compatibility: theme-color-level() function
+// Positive levels darken (mix with black), negative levels lighten (mix with white)
+// Each level is 8% change, matching Bootstrap 4 behavior
+@function theme-color-level($variant, $level: 0) {
+ $color: map-get($theme-colors, $variant);
+ $weight: $level * 8%;
+
+ @if $level > 0 {
+ @return shade-color($color, $weight);
+ } @else if $level < 0 {
+ @return tint-color($color, math.abs($weight));
+ } @else {
+ @return $color;
+ }
}
\ No newline at end of file
diff --git a/src/assets/styles/bootstrap/_index.scss b/src/assets/styles/bootstrap/_index.scss
index 847e83e..994a412 100644
--- a/src/assets/styles/bootstrap/_index.scss
+++ b/src/assets/styles/bootstrap/_index.scss
@@ -1,3 +1,18 @@
+// Import Bootstrap functions first
+@import "~bootstrap/scss/functions";
+
+// Import our custom colors and variables before Bootstrap variables
+@import "../bmc/helpers/colors";
+@import "../bmc/helpers/variables";
+
+// Import Bootstrap variables and maps
+@import "~bootstrap/scss/variables";
+@import "~bootstrap/scss/maps";
+@import "~bootstrap/scss/mixins";
+
+// Import our custom functions that depend on Bootstrap variables
+@import "../bmc/helpers/functions";
+
// Base
@import "~bootstrap/scss/root";
@import "~bootstrap/scss/reboot";
@@ -12,15 +27,11 @@
@import "~bootstrap/scss/buttons";
@import "~bootstrap/scss/card";
@import "~bootstrap/scss/close";
-@import "~bootstrap/scss/code";
-@import "~bootstrap/scss/custom-forms";
@import "~bootstrap/scss/dropdown";
@import "~bootstrap/scss/forms";
@import "~bootstrap/scss/grid";
@import "~bootstrap/scss/images";
-@import "~bootstrap/scss/input-group";
@import "~bootstrap/scss/list-group";
-@import "~bootstrap/scss/media";
@import "~bootstrap/scss/modal";
@import "~bootstrap/scss/nav";
@import "~bootstrap/scss/navbar";
@@ -33,5 +44,4 @@
@import "~bootstrap/scss/tooltip";
// Utils
-@import "~bootstrap/scss/utilities";
-@import "~bootstrap/scss/print";
\ No newline at end of file
+@import "~bootstrap/scss/utilities";
\ No newline at end of file
diff --git a/src/components/AppHeader/AppHeader.vue b/src/components/AppHeader/AppHeader.vue
index 89d561e..5b0072b 100644
--- a/src/components/AppHeader/AppHeader.vue
+++ b/src/components/AppHeader/AppHeader.vue
@@ -31,7 +31,7 @@
</b-button>
<b-navbar-nav>
<b-navbar-brand
- class="mr-0"
+ class="me-0"
to="/"
data-test-id="appHeader-container-overview"
>
@@ -42,15 +42,15 @@
:alt="altLogo"
/>
</b-navbar-brand>
- <div v-if="isNavTagPresent" :key="routerKey" class="pl-2 nav-tags">
+ <div v-if="isNavTagPresent" :key="routerKey" class="ps-2 nav-tags">
<span>|</span>
- <span class="pl-3 asset-tag">{{ assetTag }}</span>
- <span class="pl-3">{{ modelType }}</span>
- <span class="pl-3">{{ serialNumber }}</span>
+ <span class="ps-3 asset-tag">{{ assetTag }}</span>
+ <span class="ps-3">{{ modelType }}</span>
+ <span class="ps-3">{{ serialNumber }}</span>
</div>
</b-navbar-nav>
<!-- Right aligned nav items -->
- <b-navbar-nav class="ml-auto helper-menu">
+ <b-navbar-nav class="ms-auto helper-menu">
<b-nav-item
to="/logs/event-logs"
data-test-id="appHeader-container-health"
@@ -220,11 +220,17 @@
this.getEvents();
},
mounted() {
- this.$root.$on(
+ require('@/eventBus').default.$on(
'change-is-navigation-open',
(isNavigationOpen) => (this.isNavigationOpen = isNavigationOpen),
);
},
+ beforeUnmount() {
+ require('@/eventBus').default.$off(
+ 'change-is-navigation-open',
+ this.handleNavigationChange,
+ );
+ },
methods: {
getSystemInfo() {
this.$store.dispatch('global/getSystemInfo');
@@ -239,11 +245,11 @@
this.$store.dispatch('authentication/logout');
},
toggleNavigation() {
- this.$root.$emit('toggle-navigation');
+ require('@/eventBus').default.$emit('toggle-navigation');
},
setFocus(event) {
event.preventDefault();
- this.$root.$emit('skip-navigation');
+ require('@/eventBus').default.$emit('skip-navigation');
},
},
};
@@ -270,7 +276,7 @@
.navbar-text,
.nav-link,
.btn-link {
- color: color('white') !important;
+ color: $white !important;
fill: currentColor;
padding: 0.68rem 1rem !important;
@@ -299,13 +305,13 @@
.helper-menu {
@include media-breakpoint-down(sm) {
- background-color: gray('800');
+ background-color: $gray-800;
width: 100%;
justify-content: flex-end;
.nav-link,
.btn {
- padding: $spacer / 1.125 $spacer / 2;
+ padding: calc(#{$spacer} / 1.125) calc(#{$spacer} / 2);
}
.nav-link:focus,
@@ -315,8 +321,8 @@
}
.responsive-text {
- @include media-breakpoint-down(xs) {
- @include sr-only;
+ @include media-breakpoint-down(sm) {
+ @include visually-hidden;
}
}
}
@@ -334,12 +340,12 @@
}
.nav-tags {
color: theme-color-level(light, 3);
- @include media-breakpoint-down(xs) {
- @include sr-only;
+ @include media-breakpoint-down(sm) {
+ @include visually-hidden;
}
.asset-tag {
@include media-breakpoint-down($responsive-layout-bp) {
- @include sr-only;
+ @include visually-hidden;
}
}
}
@@ -364,7 +370,7 @@
}
&.open {
- background-color: gray('800');
+ background-color: $gray-800;
}
@include media-breakpoint-up($responsive-layout-bp) {
@@ -388,13 +394,13 @@
}
.navbar-brand {
- padding: $spacer/2;
+ padding: calc(#{$spacer} / 2);
height: $header-height;
line-height: 1;
&:focus {
box-shadow:
inset 0 0 0 3px $navbar-color,
- inset 0 0 0 5px color('white');
+ inset 0 0 0 5px $white;
outline: 0;
}
}
diff --git a/src/components/AppNavigation/AppNavigation.vue b/src/components/AppNavigation/AppNavigation.vue
index 45a95f5..aa8598b 100644
--- a/src/components/AppNavigation/AppNavigation.vue
+++ b/src/components/AppNavigation/AppNavigation.vue
@@ -5,32 +5,47 @@
<b-nav vertical class="mb-4">
<template v-for="navItem in navigationItems">
<!-- Navigation items with no children -->
- <b-nav-item
+ <li
v-if="!navItem.children"
- :key="navItem.index"
- :to="navItem.route"
- :data-test-id="`nav-item-${navItem.id}`"
+ :key="`nav-${navItem.index}`"
+ class="nav-item"
>
- <component :is="navItem.icon" />
- {{ navItem.label }}
- </b-nav-item>
+ <router-link
+ :to="navItem.route"
+ :data-test-id="`nav-item-${navItem.id}`"
+ class="nav-link"
+ >
+ <component :is="navItem.icon" />
+ {{ navItem.label }}
+ </router-link>
+ </li>
<!-- Navigation items with children -->
- <li v-else :key="navItem.index" class="nav-item">
+ <li v-else :key="`nav-group-${navItem.index}`" class="nav-item">
<b-button
- v-b-toggle="`${navItem.id}`"
+ :class="{ collapsed: !isItemOpen(navItem.id) }"
variant="link"
:data-test-id="`nav-button-${navItem.id}`"
+ :aria-controls="navItem.id"
+ :aria-expanded="isItemOpen(navItem.id) ? 'true' : 'false'"
+ @click="toggleCollapse(navItem.id)"
>
<component :is="navItem.icon" />
{{ navItem.label }}
<icon-expand class="icon-expand" />
</b-button>
- <b-collapse :id="navItem.id" tag="ul" class="nav-item__nav">
- <li class="nav-item">
+ <b-collapse
+ :id="navItem.id"
+ v-model="openSections[navItem.id]"
+ tag="ul"
+ class="nav-item__nav"
+ >
+ <li
+ v-for="(subNavItem, i) in filteredNavItem(navItem.children)"
+ :key="i"
+ class="nav-item"
+ >
<router-link
- v-for="(subNavItem, i) of filteredNavItem(navItem.children)"
- :key="i"
:to="subNavItem.route"
:data-test-id="`nav-item-${subNavItem.id}`"
class="nav-link"
@@ -70,21 +85,50 @@
$t: useI18n().t,
isNavigationOpen: false,
currentUserRole: null,
+ openSections: {},
};
},
watch: {
$route: function () {
this.isNavigationOpen = false;
+ // Ensure the parent section of the current route is expanded
+ this.initializeOpenSectionsFromRoute();
},
isNavigationOpen: function (isNavigationOpen) {
- this.$root.$emit('change-is-navigation-open', isNavigationOpen);
+ require('@/eventBus').default.$emit(
+ 'change-is-navigation-open',
+ isNavigationOpen,
+ );
},
},
mounted() {
this.getPrivilege();
- this.$root.$on('toggle-navigation', () => this.toggleIsOpen());
+ require('@/eventBus').default.$on('toggle-navigation', () =>
+ this.toggleIsOpen(),
+ );
+ // Expand the parent section for the current route on initial load/refresh
+ this.initializeOpenSectionsFromRoute();
+ },
+ beforeUnmount() {
+ require('@/eventBus').default.$off(
+ 'toggle-navigation',
+ this.handleToggleNavigation,
+ );
},
methods: {
+ isItemOpen(id) {
+ return !!this.openSections[id];
+ },
+ toggleCollapse(id) {
+ if (this.$set) {
+ this.$set(this.openSections, id, !this.openSections[id]);
+ } else {
+ this.openSections = {
+ ...this.openSections,
+ [id]: !this.openSections[id],
+ };
+ }
+ },
toggleIsOpen() {
this.isNavigationOpen = !this.isNavigationOpen;
},
@@ -99,6 +143,20 @@
});
} else return navItem;
},
+ initializeOpenSectionsFromRoute() {
+ const currentPath = this.$route?.path;
+ if (!currentPath) return;
+ const sectionsToOpen = {};
+ for (const item of this.navigationItems) {
+ if (
+ item.children &&
+ item.children.some((child) => child.route === currentPath)
+ ) {
+ sectionsToOpen[item.id] = true;
+ }
+ }
+ this.openSections = { ...this.openSections, ...sectionsToOpen };
+ },
},
};
</script>
@@ -108,15 +166,15 @@
fill: currentColor;
height: 1.2rem;
width: 1.2rem;
- margin-left: 0 !important; //!important overriding button specificity
+ margin-inline-start: 0 !important; //!important overriding button specificity
vertical-align: text-bottom;
&:not(.icon-expand) {
- margin-right: $spacer;
+ margin-inline-end: $spacer;
}
}
.nav {
- padding-top: $spacer / 4;
+ padding-top: calc(#{$spacer} / 4);
@include media-breakpoint-up($responsive-layout-bp) {
padding-top: $spacer;
}
@@ -124,15 +182,16 @@
.nav-item__nav {
list-style: none;
- padding-left: 0;
- margin-left: 0;
+ padding-inline-start: 0;
+ margin-inline-start: 0;
.nav-item {
outline: none;
+ list-style: none;
}
.nav-link {
- padding-left: $spacer * 4;
+ padding-inline-start: $spacer * 4;
outline: none;
&:not(.nav-link--current) {
@@ -144,7 +203,7 @@
.btn-link {
display: inline-block;
width: 100%;
- text-align: left;
+ text-align: start;
text-decoration: none !important;
border-radius: 0;
@@ -156,16 +215,16 @@
}
.icon-expand {
- float: right;
- margin-top: $spacer / 4;
+ float: inline-end;
+ margin-top: calc(#{$spacer} / 4);
}
.btn-link,
.nav-link {
position: relative;
font-weight: $headings-font-weight;
- padding-left: $spacer; // defining consistent padding for links and buttons
- padding-right: $spacer;
+ padding-inline-start: $spacer; // defining consistent padding for links and buttons
+ padding-inline-end: $spacer;
color: theme-color('secondary');
&:hover {
@@ -198,7 +257,7 @@
position: absolute;
top: 0;
bottom: 0;
- left: 0;
+ inset-inline-start: 0;
width: 4px;
background-color: theme-color('primary');
}
@@ -221,7 +280,7 @@
background-color: theme-color('light');
transform: translateX(-$navigation-width);
transition: transform $exit-easing--productive $duration--moderate-02;
- border-right: 1px solid theme-color-level('light', 2.85);
+ border-inline-end: 1px solid theme-color-level('light', 2.85);
@include media-breakpoint-down(md) {
z-index: $zindex-fixed + 2;
diff --git a/src/components/Global/Alert.vue b/src/components/Global/Alert.vue
index e8de9e2..a66d112 100644
--- a/src/components/Global/Alert.vue
+++ b/src/components/Global/Alert.vue
@@ -24,7 +24,7 @@
<script>
import StatusIcon from '@/components/Global/StatusIcon';
-import { BAlert } from 'bootstrap-vue';
+import { BAlert } from 'bootstrap-vue-next';
export default {
name: 'Alert',
diff --git a/src/components/Global/ButtonBackToTop.vue b/src/components/Global/ButtonBackToTop.vue
index 6d2f740..be6c75d 100644
--- a/src/components/Global/ButtonBackToTop.vue
+++ b/src/components/Global/ButtonBackToTop.vue
@@ -8,7 +8,9 @@
@click="scrollToTop"
>
<icon-up-to-top />
- <span class="sr-only">{{ $t('global.ariaLabel.scrollToTop') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.ariaLabel.scrollToTop') }}
+ </span>
</b-button>
</template>
diff --git a/src/components/Global/ConfirmModal.vue b/src/components/Global/ConfirmModal.vue
new file mode 100644
index 0000000..8a9a7b8
--- /dev/null
+++ b/src/components/Global/ConfirmModal.vue
@@ -0,0 +1,54 @@
+<template>
+ <!-- Simplified ConfirmModal using native Window.confirm() -->
+ <!-- This component preserves the API for future proper modal implementation -->
+ <div style="display: none"></div>
+</template>
+
+<script>
+export default {
+ name: 'ConfirmModal',
+ data() {
+ return {
+ resolve: null,
+ };
+ },
+ created() {
+ const bus = require('@/eventBus').default;
+ bus.$on('confirm:open', this.handleConfirm);
+ },
+ beforeUnmount() {
+ require('@/eventBus').default.$off('confirm:open', this.handleConfirm);
+ },
+ methods: {
+ handleConfirm(options) {
+ // Extract message from options (could be string or object)
+ const message =
+ typeof options === 'string'
+ ? options
+ : options.message || 'Are you sure?';
+
+ // Use native browser confirm for now
+ // The following parameters are accepted but not used by the window.confirm() shim.
+ // They will be used when the proper Bootstrap 5 modal is implemented:
+ // - title: Modal title text
+ // - okTitle: OK/Confirm button text
+ // - cancelTitle: Cancel button text
+ // - okVariant: OK button Bootstrap variant (e.g., 'danger', 'primary')
+ // - cancelVariant: Cancel button Bootstrap variant (e.g., 'secondary')
+ // - autoFocusButton: Which button to focus ('ok' or 'cancel')
+ // - processing: Show processing state with progress bar
+ // - processingText: Processing state message
+ // - processingMax: Processing progress bar maximum value
+ //
+ // Code can safely pass these parameters now and they will work when the
+ // proper modal implementation is added.
+ const result = window.confirm(message);
+
+ // Resolve the promise with result
+ if (options.resolve) {
+ options.resolve(result);
+ }
+ },
+ },
+};
+</script>
diff --git a/src/components/Global/FormFile.vue b/src/components/Global/FormFile.vue
index c337bf1..57eface 100644
--- a/src/components/Global/FormFile.vue
+++ b/src/components/Global/FormFile.vue
@@ -1,37 +1,39 @@
<template>
<div class="custom-form-file-container">
- <label>
- <b-form-file
- :id="id"
- v-model="file"
- :accept="accept"
- :disabled="disabled"
- :state="state"
- plain
- @input="$emit('input', $event)"
- >
- </b-form-file>
- <span
- class="add-file-btn btn"
- :class="{
- disabled,
- 'btn-secondary': isSecondary,
- 'btn-primary': !isSecondary,
- }"
- >
- {{ $t('global.fileUpload.browseText') }}
- </span>
- <slot name="invalid"></slot>
- </label>
+ <b-form-file
+ :id="id"
+ ref="fileInput"
+ v-model="file"
+ :accept="accept"
+ :disabled="disabled"
+ :state="state"
+ plain
+ @input="$emit('input', $event)"
+ >
+ </b-form-file>
+ <button
+ type="button"
+ class="add-file-btn btn mt-2"
+ :class="{
+ disabled,
+ 'btn-secondary': isSecondary,
+ 'btn-primary': !isSecondary,
+ }"
+ :disabled="disabled"
+ @click="openFilePicker"
+ >
+ {{ $t('global.fileUpload.browseText') }}
+ </button>
+ <slot name="invalid"></slot>
<div v-if="file" class="clear-selected-file px-3 py-2 mt-2">
{{ file ? file.name : '' }}
<b-button
variant="light"
- class="px-2 ml-auto"
+ class="px-2 ms-auto"
:disabled="disabled"
@click="file = null"
><icon-close :title="$t('global.fileUpload.clearSelectedFile')" /><span
- class="sr-only"
+ class="visually-hidden-focusable"
>{{ $t('global.fileUpload.clearSelectedFile') }}</span
>
</b-button>
@@ -40,7 +42,7 @@
</template>
<script>
-import { BFormFile } from 'bootstrap-vue';
+import { BFormFile } from 'bootstrap-vue-next';
import IconClose from '@carbon/icons-vue/es/close/20';
import { useI18n } from 'vue-i18n';
@@ -81,36 +83,48 @@
return this.variant === 'secondary';
},
},
+ methods: {
+ openFilePicker() {
+ // Access the native input element within the BFormFile component
+ const fileInput = document.getElementById(this.id);
+ if (fileInput) {
+ fileInput.click();
+ }
+ },
+ },
};
</script>
<style lang="scss" scoped>
-.form-control-file {
+// Hide the native file input but keep it accessible
+:deep(.form-control),
+:deep(input[type='file']) {
opacity: 0;
height: 0;
- &:focus + span {
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+}
+
+.add-file-btn {
+ &.disabled {
+ border-color: $gray-400;
+ background-color: $gray-400;
+ color: $gray-600;
+ box-shadow: none !important;
+ }
+ &:focus {
box-shadow:
inset 0 0 0 3px theme-color('primary'),
inset 0 0 0 5px $white;
}
}
-// Get mouse pointer on complete element
-.add-file-btn {
- position: relative;
- &.disabled {
- border-color: gray('400');
- background-color: gray('400');
- color: gray('600');
- box-shadow: none !important;
- }
-}
-
.clear-selected-file {
display: flex;
align-items: center;
background-color: theme-color('light');
- word-break: break-all; // break long file name into multiple lines
+ word-break: break-all;
.btn {
width: 36px;
height: 36px;
diff --git a/src/components/Global/InfoTooltip.vue b/src/components/Global/InfoTooltip.vue
index fc80216..cd7acc1 100644
--- a/src/components/Global/InfoTooltip.vue
+++ b/src/components/Global/InfoTooltip.vue
@@ -6,7 +6,9 @@
:title="title"
>
<icon-tooltip />
- <span class="sr-only">{{ $t('global.ariaLabel.tooltip') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.ariaLabel.tooltip') }}
+ </span>
</b-button>
</template>
diff --git a/src/components/Global/InputPasswordToggle.vue b/src/components/Global/InputPasswordToggle.vue
index b682cd5..4212dc2 100644
--- a/src/components/Global/InputPasswordToggle.vue
+++ b/src/components/Global/InputPasswordToggle.vue
@@ -10,7 +10,7 @@
>
<icon-view-off v-if="isVisible" />
<icon-view v-else />
- <span class="sr-only">{{ togglePasswordLabel }}</span>
+ <span class="visually-hidden">{{ togglePasswordLabel }}</span>
</b-button>
</div>
</template>
@@ -31,8 +31,7 @@
},
methods: {
toggleVisibility() {
- const firstChild = this.$children[0];
- const inputEl = firstChild ? firstChild.$el : null;
+ const inputEl = this.$el.querySelector('input');
this.isVisible = !this.isVisible;
@@ -55,5 +54,6 @@
<style lang="scss" scoped>
.input-password-toggle-container {
position: relative;
+ display: inline-block;
}
</style>
diff --git a/src/components/Global/LoadingBar.vue b/src/components/Global/LoadingBar.vue
index 8b63093..9297690 100644
--- a/src/components/Global/LoadingBar.vue
+++ b/src/components/Global/LoadingBar.vue
@@ -24,16 +24,21 @@
};
},
created() {
- this.$root.$on('loader-start', () => {
+ this.$eventBus.on('loader-start', () => {
this.startLoadingInterval();
});
- this.$root.$on('loader-end', () => {
+ this.$eventBus.on('loader-end', () => {
this.endLoadingInterval();
});
- this.$root.$on('loader-hide', () => {
+ this.$eventBus.on('loader-hide', () => {
this.hideLoadingBar();
});
},
+ beforeUnmount() {
+ this.$eventBus.off('loader-start', this.handleLoaderStart);
+ this.$eventBus.off('loader-end', this.handleLoaderEnd);
+ this.$eventBus.off('loader-hide', this.handleLoaderHide);
+ },
methods: {
startLoadingInterval() {
this.clearLoadingInterval();
diff --git a/src/components/Global/PageContainer.vue b/src/components/Global/PageContainer.vue
index ab4adb6..762a0d7 100644
--- a/src/components/Global/PageContainer.vue
+++ b/src/components/Global/PageContainer.vue
@@ -10,10 +10,18 @@
name: 'PageContainer',
mixins: [JumpLinkMixin],
created() {
- this.$root.$on('skip-navigation', () => {
+ // Use global event bus instead of removed $root.$on
+ const eventBus = require('@/eventBus').default;
+ eventBus.$on('skip-navigation', () => {
this.setFocus(this.$el);
});
},
+ beforeUnmount() {
+ require('@/eventBus').default.$off(
+ 'skip-navigation',
+ this.handleSkipNavigation,
+ );
+ },
};
</script>
<style lang="scss" scoped>
@@ -22,8 +30,8 @@
height: 100%;
padding-top: $spacer * 1.5;
padding-bottom: $spacer * 3;
- padding-left: $spacer;
- padding-right: $spacer;
+ padding-inline-start: $spacer;
+ padding-inline-end: $spacer;
&:focus-visible {
box-shadow: inset 0 0 0 2px theme-color('primary');
@@ -31,7 +39,7 @@
}
@include media-breakpoint-up($responsive-layout-bp) {
- padding-left: $spacer * 2;
+ padding-inline-start: $spacer * 2;
}
}
</style>
diff --git a/src/components/Global/Search.vue b/src/components/Global/Search.vue
index dcc1ca0..82d9719 100644
--- a/src/components/Global/Search.vue
+++ b/src/components/Global/Search.vue
@@ -2,16 +2,18 @@
<div class="search-global">
<b-form-group
:label="$t('global.form.search')"
- :label-for="`searchInput-${_uid}`"
+ :label-for="`searchInput-${uid}`"
label-class="invisible"
class="mb-2"
>
<b-input-group size="md" class="align-items-center">
- <b-input-group-prepend>
- <icon-search class="search-icon" />
- </b-input-group-prepend>
+ <template #prepend>
+ <b-input-group-text>
+ <icon-search class="search-icon" />
+ </b-input-group-text>
+ </template>
<b-form-input
- :id="`searchInput-${_uid}`"
+ :id="`searchInput-${uid}`"
ref="searchInput"
v-model="filter"
class="search-input"
@@ -29,7 +31,9 @@
@click="onClearSearch"
>
<icon-close />
- <span class="sr-only">{{ $t('global.ariaLabel.clearSearch') }}</span>
+ <span class="visually-hidden">
+ {{ $t('global.ariaLabel.clearSearch') }}
+ </span>
</b-button>
</b-input-group>
</b-form-group>
@@ -58,6 +62,7 @@
return {
$t: useI18n().t,
filter: null,
+ uid: Math.random().toString(36).slice(2),
};
},
methods: {
@@ -67,21 +72,15 @@
onClearSearch() {
this.filter = '';
this.$emit('clear-search');
- this.$refs.searchInput.focus();
+ const input = this.$refs.searchInput;
+ if (input && typeof input.focus === 'function') input.focus();
},
},
};
</script>
<style lang="scss" scoped>
-.search-input {
- padding-left: ($spacer * 2);
-}
.search-icon {
- position: absolute;
- left: 10px;
- top: 12px;
- z-index: 4;
- stroke: gray('400');
+ stroke: $gray-400;
}
</style>
diff --git a/src/components/Global/StatusIcon.vue b/src/components/Global/StatusIcon.vue
index 4552633..9044e52 100644
--- a/src/components/Global/StatusIcon.vue
+++ b/src/components/Global/StatusIcon.vue
@@ -47,7 +47,7 @@
color: theme-color('danger');
}
&.secondary {
- color: gray('600');
+ color: $gray-600;
transform: rotate(-45deg);
}
&.warning {
diff --git a/src/components/Global/TableDateFilter.vue b/src/components/Global/TableDateFilter.vue
index ca1c852..6d11a07 100644
--- a/src/components/Global/TableDateFilter.vue
+++ b/src/components/Global/TableDateFilter.vue
@@ -4,13 +4,13 @@
<b-form-group
:label="$t('global.table.fromDate')"
label-for="input-from-date"
- class="mr-3 my-0 w-100"
+ class="me-3 my-0 w-100"
>
<b-input-group>
<b-form-input
id="input-from-date"
v-model="fromDate"
- placeholder="YYYY-MM-DD"
+ type="date"
:state="getValidationState(v$.fromDate)"
class="form-control-with-button mb-3 mb-md-0"
@blur="v$.fromDate.$touch()"
@@ -20,31 +20,13 @@
{{ $t('global.form.invalidFormat') }}
</template>
<template v-if="v$.fromDate.maxDate.$invalid">
- {{ $t('global.form.dateMustBeBefore', { date: toDate }) }}
+ {{
+ $t('global.form.dateMustBeBefore', {
+ date: toDate,
+ })
+ }}
</template>
</b-form-invalid-feedback>
- <b-form-datepicker
- v-model="fromDate"
- class="btn-datepicker btn-icon-only"
- button-only
- right
- :max="toDate"
- :hide-header="true"
- :locale="locale"
- :label-help="
- $t('global.calendar.useCursorKeysToNavigateCalendarDates')
- "
- :title="$t('global.calendar.selectDate')"
- button-variant="link"
- aria-controls="input-from-date"
- >
- <template #button-content>
- <icon-calendar />
- <span class="sr-only">
- {{ $t('global.calendar.selectDate') }}
- </span>
- </template>
- </b-form-datepicker>
</b-input-group>
</b-form-group>
<b-form-group
@@ -56,7 +38,7 @@
<b-form-input
id="input-to-date"
v-model="toDate"
- placeholder="YYYY-MM-DD"
+ type="date"
:state="getValidationState(v$.toDate)"
class="form-control-with-button"
@blur="v$.toDate.$touch()"
@@ -66,31 +48,13 @@
{{ $t('global.form.invalidFormat') }}
</template>
<template v-if="v$.toDate.minDate.$invalid">
- {{ $t('global.form.dateMustBeAfter', { date: fromDate }) }}
+ {{
+ $t('global.form.dateMustBeAfter', {
+ date: fromDate,
+ })
+ }}
</template>
</b-form-invalid-feedback>
- <b-form-datepicker
- v-model="toDate"
- class="btn-datepicker btn-icon-only"
- button-only
- right
- :min="fromDate"
- :hide-header="true"
- :locale="locale"
- :label-help="
- $t('global.calendar.useCursorKeysToNavigateCalendarDates')
- "
- :title="$t('global.calendar.selectDate')"
- button-variant="link"
- aria-controls="input-to-date"
- >
- <template #button-content>
- <icon-calendar />
- <span class="sr-only">
- {{ $t('global.calendar.selectDate') }}
- </span>
- </template>
- </b-form-datepicker>
</b-input-group>
</b-form-group>
</b-col>
@@ -98,7 +62,6 @@
</template>
<script>
-import IconCalendar from '@carbon/icons-vue/es/calendar/20';
import { helpers } from 'vuelidate/lib/validators';
import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
import { useVuelidate } from '@vuelidate/core';
@@ -107,7 +70,6 @@
const isoDateRegex = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/;
export default {
- components: { IconCalendar },
mixins: [VuelidateMixin],
emits: ['change'],
setup() {
diff --git a/src/components/Global/TableFilter.vue b/src/components/Global/TableFilter.vue
index 690f453..e3afb3e 100644
--- a/src/components/Global/TableFilter.vue
+++ b/src/components/Global/TableFilter.vue
@@ -3,7 +3,7 @@
<p class="d-inline-block mb-0">
<b-badge v-for="(tag, index) in tags" :key="index" pill>
{{ tag }}
- <b-button-close
+ <b-close-button
:disabled="dropdownVisible"
:aria-hidden="true"
@click="removeTag(tag)"
@@ -22,7 +22,7 @@
<icon-filter />
{{ $t('global.action.filter') }}
</template>
- <b-dropdown-form>
+ <div class="px-3 py-2">
<b-form-group
v-for="(filter, index) of filters"
:key="index"
@@ -35,20 +35,21 @@
:value="value"
:data-test-id="`tableFilter-checkbox-${value}`"
>
- <b-dropdown-item>
- {{ value }}
- </b-dropdown-item>
+ <span class="dropdown-item-text">{{ value }}</span>
</b-form-checkbox>
</b-form-checkbox-group>
</b-form-group>
- </b-dropdown-form>
- <b-dropdown-item-button
- variant="primary"
- data-test-id="tableFilter-button-clearAll"
- @click="clearAllTags"
- >
- {{ $t('global.action.clearAll') }}
- </b-dropdown-item-button>
+ </div>
+ <div class="px-3 pb-2">
+ <b-button
+ size="sm"
+ variant="primary"
+ data-test-id="tableFilter-button-clearAll"
+ @click="clearAllTags"
+ >
+ {{ $t('global.action.clearAll') }}
+ </b-button>
+ </div>
</b-dropdown>
</div>
</template>
@@ -113,6 +114,6 @@
<style lang="scss" scoped>
.badge {
- margin-right: $spacer / 2;
+ margin-inline-end: calc(#{$spacer} / 2);
}
</style>
diff --git a/src/components/Global/TableRowAction.vue b/src/components/Global/TableRowAction.vue
index e00a380..79162bc 100644
--- a/src/components/Global/TableRowAction.vue
+++ b/src/components/Global/TableRowAction.vue
@@ -1,8 +1,9 @@
<template>
<span>
- <b-link
+ <b-button
v-if="value === 'export'"
- class="align-bottom btn-icon-only py-0 btn-link"
+ variant="link"
+ class="align-bottom btn-icon-only py-0"
:download="download"
:href="href"
:title="title"
@@ -10,46 +11,48 @@
<slot name="icon">
{{ $t('global.action.export') }}
</slot>
- <span v-if="btnIconOnly" class="sr-only">{{ title }}</span>
- </b-link>
- <b-link
+ <span v-if="btnIconOnly" class="visually-hidden">{{ title }}</span>
+ </b-button>
+ <b-button
v-else-if="
value === 'download' && downloadInNewTab && downloadLocation !== ''
"
- class="align-bottom btn-icon-only py-0 btn-link"
+ variant="link"
+ class="align-bottom btn-icon-only py-0"
target="_blank"
:href="downloadLocation"
:title="title"
>
<slot name="icon" />
- <span class="sr-only">
+ <span class="visually-hidden">
{{ $t('global.action.download') }}
</span>
- </b-link>
- <b-link
+ </b-button>
+ <b-button
v-else-if="value === 'download' && downloadLocation !== ''"
- class="align-bottom btn-icon-only py-0 btn-link"
+ variant="link"
+ class="align-bottom btn-icon-only py-0"
:download="exportName"
:href="downloadLocation"
:title="title"
>
<slot name="icon" />
- <span class="sr-only">
+ <span class="visually-hidden">
{{ $t('global.action.download') }}
</span>
- </b-link>
+ </b-button>
<b-button
v-else-if="showButton"
variant="link"
:class="{ 'btn-icon-only': btnIconOnly }"
:disabled="!enabled"
- :title="btnIconOnly ? title : !title"
+ :title="title"
@click="$emit('click-table-action', value)"
>
<slot name="icon">
{{ title }}
</slot>
- <span v-if="btnIconOnly" class="sr-only">{{ title }}</span>
+ <span v-if="btnIconOnly" class="visually-hidden">{{ title }}</span>
</b-button>
</span>
</template>
diff --git a/src/components/Global/TableToolbar.vue b/src/components/Global/TableToolbar.vue
index 373b90a..ad7c996 100644
--- a/src/components/Global/TableToolbar.vue
+++ b/src/components/Global/TableToolbar.vue
@@ -101,7 +101,7 @@
// Using v-deep to style export slot child-element
// depricated and vue-js 3
-.toolbar-actions ::v-deep .btn {
+.toolbar-actions :deep(.btn) {
position: relative;
&:after {
content: '';
diff --git a/src/components/Mixins/BVPaginationMixin.js b/src/components/Mixins/BVPaginationMixin.js
index 1aa20a5..0834ae7 100644
--- a/src/components/Mixins/BVPaginationMixin.js
+++ b/src/components/Mixins/BVPaginationMixin.js
@@ -24,6 +24,15 @@
},
];
const BVPaginationMixin = {
+ watch: {
+ perPage(newPerPage) {
+ // When switching to "View all" (perPage === 0), reset to first page
+ // to avoid empty views when previously on a later page.
+ if (newPerPage === 0) {
+ this.currentPage = 1;
+ }
+ },
+ },
methods: {
getTotalRowCount(count) {
return this.perPage === 0 ? 0 : count;
diff --git a/src/components/Mixins/BVTableSelectableMixin.js b/src/components/Mixins/BVTableSelectableMixin.js
index b4f0b95..48f5073 100644
--- a/src/components/Mixins/BVTableSelectableMixin.js
+++ b/src/components/Mixins/BVTableSelectableMixin.js
@@ -3,38 +3,123 @@
export const tableHeaderCheckboxIndeterminate = false;
const BVTableSelectableMixin = {
+ data() {
+ return {
+ selectedRows: [],
+ tableHeaderCheckboxModel: false,
+ tableHeaderCheckboxIndeterminate: false,
+ };
+ },
+ watch: {
+ currentPage() {
+ // Bootstrap Vue 2 behavior: Clear selections when page changes
+ // This prevents confusion with checkboxes appearing checked on the new page
+ const table = this.$refs.table;
+ if (table) {
+ table.clearSelected();
+ this.selectedRows = [];
+ this.tableHeaderCheckboxModel = false;
+ this.tableHeaderCheckboxIndeterminate = false;
+ }
+ },
+ },
methods: {
clearSelectedRows(tableRef) {
- if (tableRef) tableRef.clearSelected();
+ if (tableRef) {
+ tableRef.clearSelected();
+ this.selectedRows = [];
+ this.tableHeaderCheckboxModel = false;
+ this.tableHeaderCheckboxIndeterminate = false;
+ }
},
toggleSelectRow(tableRef, rowIndex) {
if (tableRef && rowIndex !== undefined) {
- tableRef.isRowSelected(rowIndex)
- ? tableRef.unselectRow(rowIndex)
- : tableRef.selectRow(rowIndex);
- }
- },
- onRowSelected(selectedRows, totalRowsCount) {
- if (selectedRows && totalRowsCount !== undefined) {
- this.selectedRows = selectedRows;
- if (selectedRows.length === 0) {
- this.tableHeaderCheckboxIndeterminate = false;
- this.tableHeaderCheckboxModel = false;
- } else if (selectedRows.length === totalRowsCount) {
- this.tableHeaderCheckboxIndeterminate = false;
- this.tableHeaderCheckboxModel = true;
+ const wasSelected = tableRef.isRowSelected(rowIndex);
+
+ if (wasSelected) {
+ tableRef.unselectRow(rowIndex);
} else {
- this.tableHeaderCheckboxIndeterminate = true;
- this.tableHeaderCheckboxModel = true;
+ tableRef.selectRow(rowIndex);
}
+
+ // Manually trigger onRowSelected after toggle since unselectRow might not fire event
+ this.$nextTick(() => {
+ this.onRowSelected();
+ });
}
},
- onChangeHeaderCheckbox(tableRef) {
- if (tableRef) {
- if (this.tableHeaderCheckboxModel) tableRef.selectAllRows();
- else tableRef.clearSelected();
+ onRowSelected() {
+ /*
+ * Bootstrap Vue Next fires @row-selected for each individual row change.
+ * Query the table's internal state to get ALL currently selected rows.
+ */
+ const table = this.$refs.table;
+ if (!table) return;
+
+ const allItems = table.filteredItems || table.items || [];
+ const selectedItems = allItems.filter((item, index) => {
+ return table.isRowSelected(index);
+ });
+
+ this.selectedRows = selectedItems;
+
+ // Update header checkbox state
+ const currentPage = this.currentPage || 1;
+ const perPage = this.perPage || 10;
+ const startIndex = (currentPage - 1) * perPage;
+ const endIndex = Math.min(startIndex + perPage, allItems.length);
+ const pageItemsCount = endIndex - startIndex;
+
+ const selectedOnPageCount = selectedItems.filter((item) =>
+ allItems
+ .slice(startIndex, endIndex)
+ .some((pageItem) => pageItem === item),
+ ).length;
+
+ if (selectedOnPageCount === 0) {
+ this.tableHeaderCheckboxIndeterminate = false;
+ this.tableHeaderCheckboxModel = false;
+ } else if (selectedOnPageCount === pageItemsCount) {
+ this.tableHeaderCheckboxIndeterminate = false;
+ this.tableHeaderCheckboxModel = true;
+ } else {
+ this.tableHeaderCheckboxIndeterminate = true;
+ this.tableHeaderCheckboxModel = true;
}
},
+ onChangeHeaderCheckbox(tableRef, event) {
+ /*
+ * Bootstrap Vue Next Migration:
+ * Handle header checkbox to select/deselect all rows on current page.
+ */
+ if (!tableRef) return;
+
+ // Extract checked state from event (could be boolean or Event object)
+ const isChecked =
+ typeof event === 'boolean' ? event : event?.target?.checked;
+
+ if (isChecked) {
+ // Select all rows on the current page
+ const currentPage = this.currentPage || 1;
+ const perPage = this.perPage || 10;
+ const startIndex = (currentPage - 1) * perPage;
+ const allItems = tableRef.filteredItems || tableRef.items || [];
+ const endIndex = Math.min(startIndex + perPage, allItems.length);
+
+ for (let i = startIndex; i < endIndex; i++) {
+ tableRef.selectRow(i);
+ }
+ } else {
+ // Deselect all rows
+ tableRef.clearSelected();
+ // Manually trigger update since clearSelected might not fire @row-selected
+ this.selectedRows = [];
+ this.tableHeaderCheckboxModel = false;
+ this.tableHeaderCheckboxIndeterminate = false;
+ }
+
+ // onRowSelected will be triggered automatically for selections
+ },
},
};
diff --git a/src/components/Mixins/BVToastMixin.js b/src/components/Mixins/BVToastMixin.js
index c8b58da..0d9fff5 100644
--- a/src/components/Mixins/BVToastMixin.js
+++ b/src/components/Mixins/BVToastMixin.js
@@ -1,58 +1,86 @@
+import { h } from 'vue';
import StatusIcon from '../Global/StatusIcon';
import i18n from '@/i18n';
-
const BVToastMixin = {
components: {
StatusIcon,
},
methods: {
$_BVToastMixin_createTitle(title, status) {
- const statusIcon = this.$createElement('StatusIcon', {
- props: { status },
- });
- const titleWithIcon = this.$createElement(
- 'strong',
- { class: 'toast-icon' },
- [statusIcon, title],
- );
- return titleWithIcon;
+ const statusIcon = h(StatusIcon, { status });
+ return h('strong', { class: 'toast-icon' }, [statusIcon, title]);
},
$_BVToastMixin_createBody(messageBody) {
if (Array.isArray(messageBody)) {
- return messageBody.map((message) =>
- this.$createElement('p', { class: 'mb-0' }, message),
- );
+ return messageBody.map((message) => h('p', { class: 'mb-0' }, message));
} else {
- return [this.$createElement('p', { class: 'mb-0' }, messageBody)];
+ return [h('p', { class: 'mb-0' }, messageBody)];
}
},
$_BVToastMixin_createTimestamp() {
const timestamp = this.$filters.formatTime(new Date());
- return this.$createElement('p', { class: 'mt-3 mb-0' }, timestamp);
+ return h('p', { class: 'mt-3 mb-0' }, timestamp);
},
$_BVToastMixin_createRefreshAction() {
- return this.$createElement(
+ return h(
'BLink',
{
class: 'd-inline-block mt-3',
- on: {
- click: () => {
- this.$root.$emit('refresh-application');
- },
+ onClick: () => {
+ require('@/eventBus').default.$emit('refresh-application');
},
},
i18n.global.t('global.action.refresh'),
);
},
$_BVToastMixin_initToast(body, title, variant) {
- this.$root.$bvToast.toast(body, {
- title,
- variant,
- autoHideDelay: 10000, //auto hide in milliseconds
- noAutoHide: variant !== 'success',
- isStatus: true,
- solid: true,
- });
+ // Use global toast plugin (works with Options API)
+ // Extract text content from VNodes for display
+
+ // Extract title text from VNode
+ const titleText =
+ typeof title === 'string'
+ ? title
+ : title?.children?.[1] || title?.children || '';
+
+ // Extract body text from VNode array
+ // Each VNode (paragraph) should be on its own line
+ const bodyLines = Array.isArray(body)
+ ? body.map((node) => {
+ if (typeof node === 'string') return node;
+ // Extract text from VNode children
+ const text = node?.children || node?.props?.children || '';
+ // Ensure timestamps and other paragraphs are on separate lines
+ return text;
+ })
+ : [typeof body === 'string' ? body : body?.children || ''];
+
+ // Join with newlines to ensure timestamps appear on their own line
+ const bodyText = bodyLines.filter(Boolean).join('\n');
+
+ // Show toast via global plugin
+ if (this.$toast) {
+ this.$toast.show({
+ body: bodyText,
+ props: {
+ title: titleText,
+ variant,
+ isStatus: true,
+ solid: false, // Use light backgrounds with dark text (not solid colors)
+ // Success toasts auto-dismiss after 10s, others stay until closed
+ interval: variant === 'success' ? 10000 : 0,
+ // Note: Progress bar hidden via CSS in _toasts.scss (JS props to hide progress bar don't work as documented in Bootstrap Vue Next 0.40.8)
+ },
+ });
+ } else {
+ // Fallback: log to console
+ /* eslint-disable no-console */
+ console[variant === 'danger' ? 'error' : 'log'](
+ `[toast:${variant}]`,
+ bodyText,
+ );
+ /* eslint-enable no-console */
+ }
},
successToast(
message,
@@ -65,7 +93,10 @@
const body = this.$_BVToastMixin_createBody(message);
const title = this.$_BVToastMixin_createTitle(t, 'success');
if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction());
- if (timestamp) body.push(this.$_BVToastMixin_createTimestamp());
+ if (timestamp) {
+ body.push(' '); // Extra newline for spacing above timestamp
+ body.push(this.$_BVToastMixin_createTimestamp());
+ }
this.$_BVToastMixin_initToast(body, title, 'success');
},
errorToast(
@@ -79,7 +110,10 @@
const body = this.$_BVToastMixin_createBody(message);
const title = this.$_BVToastMixin_createTitle(t, 'danger');
if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction());
- if (timestamp) body.push(this.$_BVToastMixin_createTimestamp());
+ if (timestamp) {
+ body.push(' '); // Extra newline for spacing above timestamp
+ body.push(this.$_BVToastMixin_createTimestamp());
+ }
this.$_BVToastMixin_initToast(body, title, 'danger');
},
warningToast(
@@ -93,7 +127,10 @@
const body = this.$_BVToastMixin_createBody(message);
const title = this.$_BVToastMixin_createTitle(t, 'warning');
if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction());
- if (timestamp) body.push(this.$_BVToastMixin_createTimestamp());
+ if (timestamp) {
+ body.push(' '); // Extra newline for spacing above timestamp
+ body.push(this.$_BVToastMixin_createTimestamp());
+ }
this.$_BVToastMixin_initToast(body, title, 'warning');
},
infoToast(
@@ -107,7 +144,10 @@
const body = this.$_BVToastMixin_createBody(message);
const title = this.$_BVToastMixin_createTitle(t, 'info');
if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction());
- if (timestamp) body.push(this.$_BVToastMixin_createTimestamp());
+ if (timestamp) {
+ body.push(' '); // Extra newline for spacing above timestamp
+ body.push(this.$_BVToastMixin_createTimestamp());
+ }
this.$_BVToastMixin_initToast(body, title, 'info');
},
},
diff --git a/src/components/Mixins/LoadingBarMixin.js b/src/components/Mixins/LoadingBarMixin.js
index d115270..b1adc78 100644
--- a/src/components/Mixins/LoadingBarMixin.js
+++ b/src/components/Mixins/LoadingBarMixin.js
@@ -3,15 +3,15 @@
const LoadingBarMixin = {
methods: {
startLoader() {
- this.$root.$emit('loader-start');
+ require('@/eventBus').default.$emit('loader-start');
this.loading = true;
},
endLoader() {
- this.$root.$emit('loader-end');
+ require('@/eventBus').default.$emit('loader-end');
this.loading = false;
},
hideLoader() {
- this.$root.$emit('loader-hide');
+ require('@/eventBus').default.$emit('loader-hide');
},
},
};
diff --git a/src/components/Mixins/TableRowExpandMixin.js b/src/components/Mixins/TableRowExpandMixin.js
index 0450877..5f56968 100644
--- a/src/components/Mixins/TableRowExpandMixin.js
+++ b/src/components/Mixins/TableRowExpandMixin.js
@@ -5,11 +5,10 @@
methods: {
toggleRowDetails(row) {
row.toggleDetails();
- row.detailsShowing
- ? (this.expandRowLabel = i18n.global.t('global.table.expandTableRow'))
- : (this.expandRowLabel = i18n.global.t(
- 'global.table.collapseTableRow',
- ));
+ // When details are shown, label should instruct to collapse; otherwise, expand
+ this.expandRowLabel = row.detailsShowing
+ ? i18n.global.t('global.table.collapseTableRow')
+ : i18n.global.t('global.table.expandTableRow');
},
},
};
diff --git a/src/components/Mixins/VuelidateMixin.js b/src/components/Mixins/VuelidateMixin.js
index fec8525..8274df6 100644
--- a/src/components/Mixins/VuelidateMixin.js
+++ b/src/components/Mixins/VuelidateMixin.js
@@ -1,6 +1,7 @@
const VuelidateMixin = {
methods: {
getValidationState(model) {
+ if (!model) return null;
const { $dirty, $error } = model;
return $dirty ? !$error : null;
},
diff --git a/src/eventBus.js b/src/eventBus.js
index 68c120f..506510c 100644
--- a/src/eventBus.js
+++ b/src/eventBus.js
@@ -1,5 +1,24 @@
-import { createApp } from 'vue';
+import mitt from 'mitt';
-const eventBus = createApp();
+const emitter = mitt();
-export default eventBus;
+function once(event, handler) {
+ const wrapper = (...args) => {
+ emitter.off(event, wrapper);
+ handler(...args);
+ };
+ emitter.on(event, wrapper);
+}
+
+export default {
+ // Vue 2-style alias
+ $on: emitter.on,
+ $off: emitter.off,
+ $emit: emitter.emit,
+ $once: once,
+ // Plain methods used by PS4 branch
+ on: emitter.on,
+ off: emitter.off,
+ emit: emitter.emit,
+ once,
+};
diff --git a/src/layouts/AppLayout.vue b/src/layouts/AppLayout.vue
index 2002269..7a2c342 100644
--- a/src/layouts/AppLayout.vue
+++ b/src/layouts/AppLayout.vue
@@ -44,7 +44,9 @@
},
},
mounted() {
- this.$root.$on('refresh-application', () => this.refresh());
+ require('@/eventBus').default.$on('refresh-application', () =>
+ this.refresh(),
+ );
setInterval(() => {
if (!localStorage.getItem('storedUsername')) {
this.$eventBus.$consoleWindow?.close();
@@ -52,7 +54,16 @@
}
}, 10000);
},
+ beforeUnmount() {
+ require('@/eventBus').default.$off(
+ 'refresh-application',
+ this.handleRefreshApplication,
+ );
+ },
methods: {
+ handleRefreshApplication() {
+ this.refresh();
+ },
refresh() {
// Clear all toast messages
document.querySelectorAll('.toast').forEach((toast) => {
diff --git a/src/layouts/LoginLayout.vue b/src/layouts/LoginLayout.vue
index 55b5692..1300c5f 100644
--- a/src/layouts/LoginLayout.vue
+++ b/src/layouts/LoginLayout.vue
@@ -14,7 +14,7 @@
<h1 v-if="customizableGuiName" class="h3 mb-5">
{{ customizableGuiName }}
</h1>
- <router-view class="login=form form-background" />
+ <router-view class="login-form form-background" />
</div>
</div>
<div class="login-aside">
@@ -48,7 +48,7 @@
<style lang="scss" scoped>
.login-container {
- background: gray('100');
+ background: $gray-100;
display: flex;
flex-direction: column;
gap: $spacer * 2;
@@ -66,19 +66,20 @@
.login-main {
min-height: 50vh;
padding: $spacer * 3;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
@include media-breakpoint-up('md') {
- background: gray('100');
- display: flex;
- flex-direction: column;
+ background: $gray-100;
flex: 1 1 75%;
min-height: 100vh;
- justify-content: center;
- align-items: center;
}
}
-.login-form {
+// Reach into the route component root element
+:deep(.login-form) {
@include media-breakpoint-up('md') {
max-width: 360px;
}
@@ -89,7 +90,7 @@
align-items: flex-end;
justify-content: flex-end;
gap: $spacer * 1.5;
- margin-right: $spacer * 3;
+ margin-inline-end: $spacer * 3;
margin-bottom: $spacer;
@include media-breakpoint-up('md') {
@@ -106,8 +107,8 @@
display: inline-block;
height: 2.5rem;
width: 2px;
- background-color: gray('200');
- margin-left: $spacer * 1.5;
+ background-color: $gray-200;
+ margin-inline-start: $spacer * 1.5;
vertical-align: middle;
}
}
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 091d7d9..b72e5dc 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -21,6 +21,8 @@
"save": "Save",
"saveSettings": "Save settings",
"selected": "Selected",
+ "tryAgain": "Try again",
+ "logOut": "Log out",
"ok": "OK",
"warning": "Warning",
"critical": "Critical"
@@ -86,6 +88,9 @@
"toast": {
"unAuthTitle": "Unauthorized",
"unAuthDescription": "The attempted action is not accessible from the logged in account. Contact your system administrator to check your privilege role."
+ },
+ "offline": {
+ "okToRetryCancelToLogin": "Click OK to retry connection, Cancel to return to login."
}
},
"appHeader": {
diff --git a/src/locales/ka-GE.json b/src/locales/ka-GE.json
index 93425af..cb3c3e8 100644
--- a/src/locales/ka-GE.json
+++ b/src/locales/ka-GE.json
@@ -23,7 +23,9 @@
"selected": "არჩეულია",
"ok": "დიახ",
"warning": "გაფრთხილება",
- "critical": "კრიტიკული"
+ "critical": "კრიტიკული",
+ "tryAgain": "კიდევ სცადეთ",
+ "logOut": "გასვლა"
},
"ariaLabel": {
"clearSearch": "ძებნის შეყვანის გასუფთავება",
@@ -86,6 +88,9 @@
"toast": {
"unAuthTitle": "არაავტორიზებულია",
"unAuthDescription": "ქმედება, რომელიც სცადეთ, ხელმისაწვდომი არაა ანგარიშისთვის, რომლითაც შესული ბრძანდებით. დაუკავშირდით თქვენს სისტემურ ადმინისტრატორს, რომ თქვენი პრივილეგიის როლი გადაამოწმოს"
+ },
+ "offline": {
+ "okToRetryCancelToLogin": "დააწექით OK-ს ხელახლა ცდისთვის, ან გაუქმება — შესვლის გვერდზე დასაბრუნებლად."
}
},
"appHeader": {
diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json
index 4df30b3..d6ec500 100644
--- a/src/locales/ru-RU.json
+++ b/src/locales/ru-RU.json
@@ -23,7 +23,9 @@
"selected": "Выбрано",
"ok": "Хорошо",
"warning": "Предупреждение",
- "critical": "Критический"
+ "critical": "Критический",
+ "tryAgain": "Повторить попытку",
+ "logOut": "Выйти"
},
"ariaLabel": {
"clearSearch": "Очистить поле поиска",
@@ -85,7 +87,10 @@
},
"toast": {
"unAuthTitle": "Недоступно",
- "unAuthDescription": "Действие недоступно из текущей учётной записи. Свяжитесь с Вашим системным администратором для проверки прав доступа."
+ "unAuthDescription": "Действие недоступно из текущей учётной записи. Свяжитесь с Вашим системным администратором для проверки прав доступа.",
+ "offline": {
+ "okToRetryCancelToLogin": "Нажмите ОК, чтобы повторить попытку, или Отмена для возврата на страницу входа."
+ }
}
},
"appHeader": {
@@ -1081,7 +1086,7 @@
"CW": "Кюрасао",
"CY": "Кипр",
"CZ": "Чехия",
- "CI": "Кот-д’Ивуар",
+ "CI": "Кот-д'Ивуар",
"DK": "Дания",
"DJ": "Джибути",
"DM": "Доминика",
diff --git a/src/main.js b/src/main.js
index 9637de9..91dc8ec 100644
--- a/src/main.js
+++ b/src/main.js
@@ -12,99 +12,154 @@
//dotenv customizations.
import store from './store';
import eventBus from './eventBus';
+import { ToastPlugin } from './plugins/toast';
import {
- BootstrapVue,
- AlertPlugin,
- BadgePlugin,
- ButtonPlugin,
- BVConfigPlugin,
- CardPlugin,
- CollapsePlugin,
- DropdownPlugin,
- FormPlugin,
- FormCheckboxPlugin,
- FormDatepickerPlugin,
- FormFilePlugin,
- FormGroupPlugin,
- FormInputPlugin,
- FormRadioPlugin,
- FormSelectPlugin,
- FormTagsPlugin,
- InputGroupPlugin,
- LayoutPlugin,
- LinkPlugin,
- ListGroupPlugin,
- ModalPlugin,
- NavbarPlugin,
- NavPlugin,
- PaginationPlugin,
- ProgressPlugin,
- TablePlugin,
- TabsPlugin,
- ToastPlugin,
- TooltipPlugin,
-} from 'bootstrap-vue';
+ BButton,
+ BContainer,
+ BForm,
+ BFormGroup,
+ BFormInput,
+ BFormCheckboxGroup,
+ BInputGroup,
+ BInputGroupText,
+ BFormSelect,
+ BFormSelectOption,
+ BFormFile,
+ BFormCheckbox,
+ BFormRadioGroup,
+ BFormRadio,
+ BFormText,
+ BFormTextarea,
+ BFormTags,
+ BFormInvalidFeedback,
+ BTable,
+ BToast,
+ BModal,
+ BCloseButton,
+ BAlert,
+ BCard,
+ BCardHeader,
+ BCardBody,
+ BCardFooter,
+ BCardGroup,
+ BRow,
+ BCol,
+ BBadge,
+ BSpinner,
+ BDropdown,
+ BDropdownItem,
+ BNav,
+ BNavbar,
+ BNavbarBrand,
+ BNavbarNav,
+ BNavItem,
+ BNavbarToggle,
+ BCollapse,
+ BPagination,
+ BTooltip,
+ BPopover,
+ BProgress,
+ BProgressBar,
+ BOverlay,
+ BListGroup,
+ BListGroupItem,
+ BTabs,
+ BTab,
+ BLink,
+ BOrchestrator,
+ createBootstrap,
+ vBToggle,
+ vBTooltip,
+ vBPopover,
+ vBModal,
+} from 'bootstrap-vue-next';
-const app = createApp({
- router,
- store,
- render: (h) => h(App),
-});
+const app = createApp(App);
+
+// Note: We register only the components/directives we need
+
+// Use createBootstrap for all bootstrap-vue-next plugins in 0.40.7+
+app.use(createBootstrap());
+
+app.component('BButton', BButton);
+app.component('BBtn', BButton);
+app.component('BContainer', BContainer);
+app.component('BForm', BForm);
+app.component('BFormGroup', BFormGroup);
+app.component('BFormInput', BFormInput);
+app.component('BFormCheckboxGroup', BFormCheckboxGroup);
+app.component('BInputGroup', BInputGroup);
+app.component('BInputGroupText', BInputGroupText);
+app.component('BFormSelect', BFormSelect);
+app.component('BFormSelectOption', BFormSelectOption);
+app.component('BFormFile', BFormFile);
+app.component('BFormCheckbox', BFormCheckbox);
+app.component('BFormRadioGroup', BFormRadioGroup);
+app.component('BFormRadio', BFormRadio);
+app.component('BFormText', BFormText);
+app.component('BFormTextarea', BFormTextarea);
+app.component('BFormTags', BFormTags);
+app.component('BFormInvalidFeedback', BFormInvalidFeedback);
+app.component('BTable', BTable);
+app.component('BToast', BToast);
+app.component('BModal', BModal);
+app.component('BCloseButton', BCloseButton);
+app.component('BAlert', BAlert);
+app.component('BCard', BCard);
+app.component('BCardHeader', BCardHeader);
+app.component('BCardBody', BCardBody);
+app.component('BCardFooter', BCardFooter);
+app.component('BCardGroup', BCardGroup);
+app.component('BRow', BRow);
+app.component('BCol', BCol);
+app.component('BBadge', BBadge);
+app.component('BSpinner', BSpinner);
+app.component('BDropdown', BDropdown);
+app.component('BDropdownItem', BDropdownItem);
+app.component('BNav', BNav);
+app.component('BNavbar', BNavbar);
+app.component('BNavbarBrand', BNavbarBrand);
+app.component('BNavbarNav', BNavbarNav);
+app.component('BNavItem', BNavItem);
+app.component('BNavbarToggle', BNavbarToggle);
+app.component('BCollapse', BCollapse);
+app.component('BPagination', BPagination);
+app.component('BTooltip', BTooltip);
+app.component('BPopover', BPopover);
+app.component('BProgress', BProgress);
+app.component('BProgressBar', BProgressBar);
+app.component('BOverlay', BOverlay);
+app.component('BListGroup', BListGroup);
+app.component('BListGroupItem', BListGroupItem);
+app.component('BTabs', BTabs);
+app.component('BTab', BTab);
+app.component('BLink', BLink);
+app.component('BOrchestrator', BOrchestrator);
+
+// Register BootstrapVue Next directives used in templates
+app.directive('b-toggle', vBToggle);
+app.directive('b-tooltip', vBTooltip);
+app.directive('b-popover', vBPopover);
+app.directive('b-modal', vBModal);
+
app.use(i18n);
-
app.use(router);
app.use(store);
-
-// Plugins
-app.use(BootstrapVue);
-app.use(AlertPlugin);
-app.use(BadgePlugin);
-app.use(ButtonPlugin);
-app.use(BVConfigPlugin, {
- BFormText: { textVariant: 'secondary' },
- BTable: {
- headVariant: 'light',
- footVariant: 'light',
- },
- BFormTags: {
- tagVariant: 'primary',
- addButtonVariant: 'link-primary',
- },
- BBadge: {
- variant: 'primary',
- },
-});
-
-app.use(CardPlugin);
-app.use(CollapsePlugin);
-app.use(DropdownPlugin);
-app.use(FormPlugin);
-app.use(FormCheckboxPlugin);
-app.use(FormDatepickerPlugin);
-app.use(FormFilePlugin);
-app.use(FormGroupPlugin);
-app.use(FormInputPlugin);
-app.use(FormRadioPlugin);
-app.use(FormSelectPlugin);
-app.use(FormTagsPlugin);
-app.use(InputGroupPlugin);
-app.use(LayoutPlugin);
-app.use(LayoutPlugin);
-app.use(LinkPlugin);
-app.use(ListGroupPlugin);
-app.use(ModalPlugin);
-app.use(NavbarPlugin);
-app.use(NavPlugin);
-app.use(PaginationPlugin);
-app.use(ProgressPlugin);
-app.use(TablePlugin);
-app.use(TabsPlugin);
app.use(ToastPlugin);
-app.use(TooltipPlugin);
-app.mount('#app');
-app.prototype.$eventBus = eventBus;
+app.config.globalProperties.$eventBus = eventBus;
+app.config.globalProperties.$confirm = (messageOrOptions) => {
+ return new Promise((resolve) => {
+ eventBus.$emit('confirm:open', {
+ ...(typeof messageOrOptions === 'string'
+ ? { message: messageOrOptions }
+ : messageOrOptions),
+ resolve,
+ });
+ });
+};
+
//Filters
const filter = {
formatDate(value) {
@@ -146,3 +201,5 @@
},
};
app.config.globalProperties.$filters = filter;
+
+app.mount('#app');
diff --git a/src/plugins/toast.js b/src/plugins/toast.js
new file mode 100644
index 0000000..2f3431a
--- /dev/null
+++ b/src/plugins/toast.js
@@ -0,0 +1,83 @@
+import { useToast } from 'bootstrap-vue-next';
+
+// Global toast plugin for Options API components
+// Bootstrap Vue Next's useToast is a composable that needs setup() context
+// This plugin makes it accessible globally via app.config.globalProperties
+
+let toastController = null;
+
+export const ToastPlugin = {
+ install(app) {
+ // Initialize toast controller in the app context
+ // This will be called once during app setup
+ app.mixin({
+ beforeCreate() {
+ // Only initialize once at the root
+ if (!toastController && this === this.$root) {
+ try {
+ toastController = useToast();
+ } catch (e) {
+ console.warn('Failed to initialize toast controller:', e);
+ }
+ }
+ },
+ });
+
+ // Provide global toast methods
+ app.config.globalProperties.$toast = {
+ show(options) {
+ if (toastController?.create) {
+ toastController.create(options);
+ } else {
+ console.warn('Toast controller not available:', options);
+ }
+ },
+ info(body, options = {}) {
+ this.show({
+ ...options,
+ body,
+ props: {
+ variant: 'info',
+ isStatus: true,
+ ...options.props,
+ },
+ });
+ },
+ success(body, options = {}) {
+ this.show({
+ ...options,
+ body,
+ props: {
+ variant: 'success',
+ isStatus: true,
+ interval: 10000,
+ // Note: Progress bar hidden via CSS in _toasts.scss (JS props don't work as documented in Bootstrap Vue Next 0.40.8)
+ ...options.props,
+ },
+ });
+ },
+ warning(body, options = {}) {
+ this.show({
+ ...options,
+ body,
+ props: {
+ variant: 'warning',
+ isStatus: true,
+ ...options.props,
+ },
+ });
+ },
+ danger(body, options = {}) {
+ this.show({
+ ...options,
+ body,
+ props: {
+ variant: 'danger',
+ isStatus: true,
+ ...options.props,
+ },
+ });
+ },
+ };
+ },
+};
diff --git a/src/store/index.js b/src/store/index.js
index 029df7a..d477fb7 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -1,5 +1,4 @@
-import Vue from 'vue';
-import Vuex from 'vuex';
+import { createStore } from 'vuex';
import GlobalStore from './modules/GlobalStore';
import AuthenticationStore from './modules/Authentication/AuthenticanStore';
@@ -34,9 +33,7 @@
import DateTimeStore from './modules/Settings/DateTimeStore';
import VirtualMediaStore from './modules/Operations/VirtualMediaStore';
-Vue.use(Vuex);
-
-const store = new Vuex.Store({
+const store = createStore({
state: {},
mutations: {},
actions: {},
diff --git a/src/store/modules/GlobalStore.js b/src/store/modules/GlobalStore.js
index e42367b..c64fbaf 100644
--- a/src/store/modules/GlobalStore.js
+++ b/src/store/modules/GlobalStore.js
@@ -92,7 +92,7 @@
const managers = await api
.get('/redfish/v1/Managers')
.catch((error) => console.log(error));
- bmcPath = managers.data?.Members?.[0]?.['@odata.id'];
+ bmcPath = managers?.data?.Members?.[0]?.['@odata.id'];
}
return bmcPath;
},
diff --git a/src/store/modules/Operations/BootSettingsStore.js b/src/store/modules/Operations/BootSettingsStore.js
index 9740da7..c860c7b 100644
--- a/src/store/modules/Operations/BootSettingsStore.js
+++ b/src/store/modules/Operations/BootSettingsStore.js
@@ -114,7 +114,10 @@
if (bootSource !== null || overrideEnabled !== null) {
promises.push(
- dispatch('saveBootSettings', { bootSource, overrideEnabled }),
+ dispatch('saveBootSettings', {
+ bootSource,
+ overrideEnabled,
+ }),
);
}
if (tpmEnabled !== null) {
diff --git a/src/store/modules/ResourceManagement/PowerControlStore.js b/src/store/modules/ResourceManagement/PowerControlStore.js
index 7fd8a59..5163c81 100644
--- a/src/store/modules/ResourceManagement/PowerControlStore.js
+++ b/src/store/modules/ResourceManagement/PowerControlStore.js
@@ -38,9 +38,13 @@
if (!collection || collection.length === 0) return;
return await api
.get(`${collection[0]}`)
- .then((response) => api.get(response.data.Power['@odata.id']))
.then((response) => {
- const powerControl = response.data.PowerControl;
+ const powerUri = response?.data?.Power?.['@odata.id'];
+ if (!powerUri) return;
+ return api.get(powerUri);
+ })
+ .then((response) => {
+ const powerControl = response?.data?.PowerControl;
if (!powerControl || powerControl.length === 0) return;
const powerCapUri = response.data['@odata.id'];
const powerCap = powerControl[0].PowerLimit.LimitInWatts;
diff --git a/src/store/modules/SecurityAndAccess/LdapStore.js b/src/store/modules/SecurityAndAccess/LdapStore.js
index a8114f7..54113ae 100644
--- a/src/store/modules/SecurityAndAccess/LdapStore.js
+++ b/src/store/modules/SecurityAndAccess/LdapStore.js
@@ -237,7 +237,9 @@
.patch('/redfish/v1/AccountService', data)
.then(() => dispatch('getAccountSettings'))
.then(() =>
- i18n.global.t('pageLdap.toast.successSaveRoleGroup', { groupName }),
+ i18n.global.t('pageLdap.toast.successSaveRoleGroup', {
+ groupName,
+ }),
)
.catch((error) => {
console.log(error);
diff --git a/src/store/modules/Settings/NetworkStore.js b/src/store/modules/Settings/NetworkStore.js
index d4174e3..323c672 100644
--- a/src/store/modules/Settings/NetworkStore.js
+++ b/src/store/modules/Settings/NetworkStore.js
@@ -300,7 +300,9 @@
return api
.patch(
`${await this.dispatch('global/getBmcPath')}/EthernetInterfaces/${state.selectedInterfaceId}`,
- { IPv4StaticAddresses: originalAddresses.concat(newAddress) },
+ {
+ IPv4StaticAddresses: originalAddresses.concat(newAddress),
+ },
)
.then(dispatch('getEthernetData'))
.then(() => {
@@ -331,7 +333,9 @@
return api
.patch(
`${await this.dispatch('global/getBmcPath')}/EthernetInterfaces/${state.selectedInterfaceId}`,
- { IPv6StaticAddresses: originalAddresses.concat(newAddress) },
+ {
+ IPv6StaticAddresses: originalAddresses.concat(newAddress),
+ },
)
.then(dispatch('getEthernetData'))
.then(() => {
diff --git a/src/utilities/NBDServer.js b/src/utilities/NBDServer.js
index 1f6f88f..ff57995 100644
--- a/src/utilities/NBDServer.js
+++ b/src/utilities/NBDServer.js
@@ -157,8 +157,7 @@
var view = new DataView(resp, 0, 10);
/* export size. */
var size = this.file.size;
- // eslint-disable-next-line prettier/prettier
- view.setUint32(0, Math.floor(size / (2 ** 32)));
+ view.setUint32(0, size >>> 32);
view.setUint32(4, size & 0xffffffff);
/* transmission flags: read-only */
view.setUint16(8, NBD_FLAG_HAS_FLAGS | NBD_FLAG_READ_ONLY);
@@ -253,8 +252,7 @@
};
this._handle_cmd_read = function (req) {
var offset;
- // eslint-disable-next-line prettier/prettier
- offset = (req.offset_msB * 2 ** 32) + req.offset_lsB;
+ offset = req.offset_msB * 0x100000000 + req.offset_lsB;
if (offset > Number.MAX_SAFE_INTEGER) return ENOSPC;
if (offset + req.length > Number.MAX_SAFE_INTEGER) return ENOSPC;
if (offset + req.length > file.size) return ENOSPC;
diff --git a/src/views/ChangePassword/ChangePassword.vue b/src/views/ChangePassword/ChangePassword.vue
index 18d9dcc..f829229 100644
--- a/src/views/ChangePassword/ChangePassword.vue
+++ b/src/views/ChangePassword/ChangePassword.vue
@@ -4,7 +4,9 @@
<p v-if="changePasswordError">
{{ $t('pageChangePassword.changePasswordError') }}
</p>
- <p v-else>{{ $t('pageChangePassword.changePasswordAlertMessage') }}</p>
+ <p v-else>
+ {{ $t('pageChangePassword.changePasswordAlertMessage') }}
+ </p>
</alert>
<div class="change-password__form-container">
<dl>
@@ -22,6 +24,7 @@
v-model="form.password"
autofocus="autofocus"
type="password"
+ autocomplete="new-password"
:state="getValidationState(v$.form.password)"
class="form-control-with-button"
@change="v$.form.password.$touch()"
@@ -43,6 +46,7 @@
id="password-confirm"
v-model="form.passwordConfirm"
type="password"
+ autocomplete="new-password"
:state="getValidationState(v$.form.passwordConfirm)"
class="form-control-with-button"
@change="v$.form.passwordConfirm.$touch()"
@@ -60,7 +64,7 @@
</b-form-invalid-feedback>
</input-password-toggle>
</b-form-group>
- <div class="text-right">
+ <div class="text-end">
<b-button type="button" variant="link" @click="goBack">
{{ $t('pageChangePassword.goBack') }}
</b-button>
diff --git a/src/views/HardwareStatus/Inventory/Inventory.vue b/src/views/HardwareStatus/Inventory/Inventory.vue
index a3f4d23..d99a792 100644
--- a/src/views/HardwareStatus/Inventory/Inventory.vue
+++ b/src/views/HardwareStatus/Inventory/Inventory.vue
@@ -153,33 +153,56 @@
created() {
this.startLoader();
const bmcManagerTablePromise = new Promise((resolve) => {
- this.$root.$on('hardware-status-bmc-manager-complete', () => resolve());
+ require('@/eventBus').default.$on(
+ 'hardware-status-bmc-manager-complete',
+ () => resolve(),
+ );
});
const chassisTablePromise = new Promise((resolve) => {
- this.$root.$on('hardware-status-chassis-complete', () => resolve());
+ require('@/eventBus').default.$on(
+ 'hardware-status-chassis-complete',
+ () => resolve(),
+ );
});
const dimmSlotTablePromise = new Promise((resolve) => {
- this.$root.$on('hardware-status-dimm-slot-complete', () => resolve());
+ require('@/eventBus').default.$on(
+ 'hardware-status-dimm-slot-complete',
+ () => resolve(),
+ );
});
const fansTablePromise = new Promise((resolve) => {
- this.$root.$on('hardware-status-fans-complete', () => resolve());
- });
- const powerSuppliesTablePromise = new Promise((resolve) => {
- this.$root.$on('hardware-status-power-supplies-complete', () =>
+ require('@/eventBus').default.$on('hardware-status-fans-complete', () =>
resolve(),
);
});
+ const powerSuppliesTablePromise = new Promise((resolve) => {
+ require('@/eventBus').default.$on(
+ 'hardware-status-power-supplies-complete',
+ () => resolve(),
+ );
+ });
const processorsTablePromise = new Promise((resolve) => {
- this.$root.$on('hardware-status-processors-complete', () => resolve());
+ require('@/eventBus').default.$on(
+ 'hardware-status-processors-complete',
+ () => resolve(),
+ );
});
const serviceIndicatorPromise = new Promise((resolve) => {
- this.$root.$on('hardware-status-service-complete', () => resolve());
+ require('@/eventBus').default.$on(
+ 'hardware-status-service-complete',
+ () => resolve(),
+ );
});
const systemTablePromise = new Promise((resolve) => {
- this.$root.$on('hardware-status-system-complete', () => resolve());
+ require('@/eventBus').default.$on('hardware-status-system-complete', () =>
+ resolve(),
+ );
});
const assemblyTablePromise = new Promise((resolve) => {
- this.$root.$on('hardware-status-assembly-complete', () => resolve());
+ require('@/eventBus').default.$on(
+ 'hardware-status-assembly-complete',
+ () => resolve(),
+ );
});
// Combine all child component Promises to indicate
// when page data load complete
diff --git a/src/views/HardwareStatus/Inventory/InventoryServiceIndicator.vue b/src/views/HardwareStatus/Inventory/InventoryServiceIndicator.vue
index 5b19b42..0a1a84b 100644
--- a/src/views/HardwareStatus/Inventory/InventoryServiceIndicator.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryServiceIndicator.vue
@@ -2,11 +2,13 @@
<page-section
:section-title="$t('pageInventory.systemIndicator.sectionTitle')"
>
- <div class="form-background pl-4 pt-4 pb-1">
+ <div class="form-background ps-4 pt-4 pb-1">
<b-row>
<b-col sm="6" md="3">
<dl>
- <dt>{{ $t('pageInventory.systemIndicator.powerStatus') }}</dt>
+ <dt>
+ {{ $t('pageInventory.systemIndicator.powerStatus') }}
+ </dt>
<dd>
{{ $t(powerStatus) }}
</dd>
@@ -68,7 +70,7 @@
created() {
this.$store.dispatch('system/getSystem').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('hardware-status-service-complete');
+ require('@/eventBus').default.$emit('hardware-status-service-complete');
});
},
methods: {
diff --git a/src/views/HardwareStatus/Inventory/InventoryTableAssembly.vue b/src/views/HardwareStatus/Inventory/InventoryTableAssembly.vue
index 68bee05..e267f7f 100644
--- a/src/views/HardwareStatus/Inventory/InventoryTableAssembly.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryTableAssembly.vue
@@ -2,9 +2,10 @@
<page-section :section-title="$t('pageInventory.assemblies')">
<b-table
sort-icon-left
- no-sort-reset
+ must-sort
hover
responsive="md"
+ thead-class="table-light"
:items="items"
:fields="fields"
show-empty
@@ -18,10 +19,11 @@
data-test-id="hardwareStatus-button-expandAssembly"
:title="expandRowLabel"
class="btn-icon-only"
+ :class="{ collapsed: !row.detailsShowing }"
@click="toggleRowDetails(row)"
>
<icon-chevron />
- <span class="sr-only">{{ expandRowLabel }}</span>
+ <span class="visually-hidden">{{ expandRowLabel }}</span>
</b-button>
</template>
@@ -134,7 +136,7 @@
created() {
this.$store.dispatch('assemblies/getAssemblyInfo').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('hardware-status-assembly-complete');
+ require('@/eventBus').default.$emit('hardware-status-assembly-complete');
this.isBusy = false;
});
},
diff --git a/src/views/HardwareStatus/Inventory/InventoryTableBmcManager.vue b/src/views/HardwareStatus/Inventory/InventoryTableBmcManager.vue
index 4ee4eba..8a0671e 100644
--- a/src/views/HardwareStatus/Inventory/InventoryTableBmcManager.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryTableBmcManager.vue
@@ -3,6 +3,7 @@
<b-table
responsive="md"
hover
+ thead-class="table-light"
:items="items"
:fields="fields"
show-empty
@@ -16,10 +17,11 @@
data-test-id="hardwareStatus-button-expandBmc"
:title="expandRowLabel"
class="btn-icon-only"
+ :class="{ collapsed: !row.detailsShowing }"
@click="toggleRowDetails(row)"
>
<icon-chevron />
- <span class="sr-only">{{ expandRowLabel }}</span>
+ <span class="visually-hidden">{{ expandRowLabel }}</span>
</b-button>
</template>
@@ -62,7 +64,9 @@
<dd>{{ dataFormatter(item.serialNumber) }}</dd>
<!-- Spare part number -->
<dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt>
- <dd>{{ dataFormatter(item.sparePartNumber) }}</dd>
+ <dd>
+ {{ dataFormatter(item.sparePartNumber) }}
+ </dd>
<!-- Model -->
<dt>{{ $t('pageInventory.table.model') }}:</dt>
<dd>{{ dataFormatter(item.model) }}</dd>
@@ -71,7 +75,9 @@
<dd>{{ dataFormatter(item.uuid) }}</dd>
<!-- Service entry point UUID -->
<dt>{{ $t('pageInventory.table.serviceEntryPointUuid') }}:</dt>
- <dd>{{ dataFormatter(item.serviceEntryPointUuid) }}</dd>
+ <dd>
+ {{ dataFormatter(item.serviceEntryPointUuid) }}
+ </dd>
</dl>
</b-col>
<b-col class="mt-2" sm="6" xl="6">
@@ -125,7 +131,7 @@
<p class="mt-1 mb-2 h6 float-none m-0">
{{ $t('pageInventory.table.graphicalConsole') }}
</p>
- <dl class="ml-4">
+ <dl class="ms-4">
<dt>{{ $t('pageInventory.table.connectTypesSupported') }}:</dt>
<dd>
{{ dataFormatterArray(item.graphicalConsoleConnectTypes) }}
@@ -211,7 +217,9 @@
created() {
this.$store.dispatch('bmc/getBmcInfo').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('hardware-status-bmc-manager-complete');
+ require('@/eventBus').default.$emit(
+ 'hardware-status-bmc-manager-complete',
+ );
this.isBusy = false;
});
},
diff --git a/src/views/HardwareStatus/Inventory/InventoryTableChassis.vue b/src/views/HardwareStatus/Inventory/InventoryTableChassis.vue
index 4458e33..8278e52 100644
--- a/src/views/HardwareStatus/Inventory/InventoryTableChassis.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryTableChassis.vue
@@ -3,6 +3,7 @@
<b-table
responsive="md"
hover
+ thead-class="table-light"
:items="chassis"
:fields="fields"
show-empty
@@ -16,10 +17,11 @@
data-test-id="hardwareStatus-button-expandChassis"
:title="expandRowLabel"
class="btn-icon-only"
+ :class="{ collapsed: !row.detailsShowing }"
@click="toggleRowDetails(row)"
>
<icon-chevron />
- <span class="sr-only">{{ expandRowLabel }}</span>
+ <span class="visually-hidden">{{ expandRowLabel }}</span>
</b-button>
</template>
@@ -178,7 +180,7 @@
created() {
this.$store.dispatch('chassis/getChassisInfo').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('hardware-status-chassis-complete');
+ require('@/eventBus').default.$emit('hardware-status-chassis-complete');
this.isBusy = false;
});
},
diff --git a/src/views/HardwareStatus/Inventory/InventoryTableDimmSlot.vue b/src/views/HardwareStatus/Inventory/InventoryTableDimmSlot.vue
index f4a850b..c767726 100644
--- a/src/views/HardwareStatus/Inventory/InventoryTableDimmSlot.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryTableDimmSlot.vue
@@ -16,15 +16,15 @@
</b-row>
<b-table
sort-icon-left
- no-sort-reset
+ must-sort
hover
- sort-by="health"
+ thead-class="table-light"
+ :sort-by="['health']"
responsive="md"
show-empty
:items="dimms"
:fields="fields"
- :sort-desc="true"
- :sort-compare="sortCompare"
+ :sort-desc="[true]"
:filter="searchFilter"
:empty-text="$t('global.table.emptyMessage')"
:empty-filtered-text="$t('global.table.emptySearchMessage')"
@@ -35,13 +35,14 @@
<template #cell(expandRow)="row">
<b-button
variant="link"
- data-test-id="hardwareStatus-button-expandDimms"
+ data-test-id="hardwareStatus-button-expandDimmSlot"
:title="expandRowLabel"
class="btn-icon-only"
+ :class="{ collapsed: !row.detailsShowing }"
@click="toggleRowDetails(row)"
>
<icon-chevron />
- <span class="sr-only">{{ expandRowLabel }}</span>
+ <span class="visually-hidden">{{ expandRowLabel }}</span>
</b-button>
</template>
@@ -95,7 +96,9 @@
<dl>
<!-- Spare Part Number -->
<dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt>
- <dd>{{ dataFormatter(item.sparePartNumber) }}</dd>
+ <dd>
+ {{ dataFormatter(item.sparePartNumber) }}
+ </dd>
</dl>
<dl>
<!-- Model -->
@@ -145,7 +148,9 @@
<dl>
<!-- Base Module Type -->
<dt>{{ $t('pageInventory.table.baseModuleType') }}:</dt>
- <dd>{{ dataFormatter(item.baseModuleType) }}</dd>
+ <dd>
+ {{ dataFormatter(item.baseModuleType) }}
+ </dd>
</dl>
</b-col>
<b-col sm="6" xl="6">
@@ -176,7 +181,9 @@
<dl>
<!-- Error Correction -->
<dt>{{ $t('pageInventory.table.errorCorrection') }}:</dt>
- <dd>{{ dataFormatter(item.errorCorrection) }}</dd>
+ <dd>
+ {{ dataFormatter(item.errorCorrection) }}
+ </dd>
</dl>
</b-col>
</b-row>
@@ -207,7 +214,13 @@
import i18n from '@/i18n';
export default {
- components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount },
+ components: {
+ IconChevron,
+ PageSection,
+ StatusIcon,
+ Search,
+ TableCellCount,
+ },
mixins: [
BVToastMixin,
TableRowExpandMixin,
@@ -271,7 +284,7 @@
created() {
this.$store.dispatch('memory/getDimms').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('hardware-status-dimm-slot-complete');
+ require('@/eventBus').default.$emit('hardware-status-dimm-slot-complete');
this.isBusy = false;
});
},
diff --git a/src/views/HardwareStatus/Inventory/InventoryTableFans.vue b/src/views/HardwareStatus/Inventory/InventoryTableFans.vue
index 4a540e0..9f4dffa 100644
--- a/src/views/HardwareStatus/Inventory/InventoryTableFans.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryTableFans.vue
@@ -16,15 +16,15 @@
</b-row>
<b-table
sort-icon-left
- no-sort-reset
+ must-sort
hover
responsive="md"
- sort-by="health"
+ thead-class="table-light"
+ :sort-by="['health']"
show-empty
:items="fans"
:fields="fields"
- :sort-desc="true"
- :sort-compare="sortCompare"
+ :sort-desc="[true]"
:filter="searchFilter"
:empty-text="$t('global.table.emptyMessage')"
:empty-filtered-text="$t('global.table.emptySearchMessage')"
@@ -38,10 +38,11 @@
data-test-id="hardwareStatus-button-expandFans"
:title="expandRowLabel"
class="btn-icon-only"
+ :class="{ collapsed: !row.detailsShowing }"
@click="toggleRowDetails(row)"
>
<icon-chevron />
- <span class="sr-only">{{ expandRowLabel }}</span>
+ <span class="visually-hidden">{{ expandRowLabel }}</span>
</b-button>
</template>
@@ -123,7 +124,13 @@
import i18n from '@/i18n';
export default {
- components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount },
+ components: {
+ IconChevron,
+ PageSection,
+ StatusIcon,
+ Search,
+ TableCellCount,
+ },
mixins: [
TableRowExpandMixin,
DataFormatterMixin,
@@ -190,7 +197,7 @@
created() {
this.$store.dispatch('fan/getFanInfo').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('hardware-status-fans-complete');
+ require('@/eventBus').default.$emit('hardware-status-fans-complete');
this.isBusy = false;
});
},
diff --git a/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue b/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue
index ddc3333..f494514 100644
--- a/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue
@@ -16,15 +16,15 @@
</b-row>
<b-table
sort-icon-left
- no-sort-reset
+ must-sort
hover
responsive="md"
- sort-by="health"
+ thead-class="table-light"
+ :sort-by="['health']"
show-empty
:items="powerSupplies"
:fields="fields"
- :sort-desc="true"
- :sort-compare="sortCompare"
+ :sort-desc="[true]"
:filter="searchFilter"
:empty-text="$t('global.table.emptyMessage')"
:empty-filtered-text="$t('global.table.emptySearchMessage')"
@@ -38,10 +38,11 @@
data-test-id="hardwareStatus-button-expandPowerSupplies"
:title="expandRowLabel"
class="btn-icon-only"
+ :class="{ collapsed: !row.detailsShowing }"
@click="toggleRowDetails(row)"
>
<icon-chevron />
- <span class="sr-only">{{ expandRowLabel }}</span>
+ <span class="visually-hidden">{{ expandRowLabel }}</span>
</b-button>
</template>
@@ -73,7 +74,9 @@
<dd>{{ dataFormatter(item.serialNumber) }}</dd>
<!-- Spare part number -->
<dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt>
- <dd>{{ dataFormatter(item.sparePartNumber) }}</dd>
+ <dd>
+ {{ dataFormatter(item.sparePartNumber) }}
+ </dd>
<!-- Model -->
<dt>{{ $t('pageInventory.table.model') }}:</dt>
<dd>{{ dataFormatter(item.model) }}</dd>
@@ -115,7 +118,9 @@
<dl>
<!-- Firmware version -->
<dt>{{ $t('pageInventory.table.firmwareVersion') }}:</dt>
- <dd>{{ dataFormatter(item.firmwareVersion) }}</dd>
+ <dd>
+ {{ dataFormatter(item.firmwareVersion) }}
+ </dd>
</dl>
</b-col>
</b-row>
@@ -144,7 +149,13 @@
import i18n from '@/i18n';
export default {
- components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount },
+ components: {
+ IconChevron,
+ PageSection,
+ StatusIcon,
+ Search,
+ TableCellCount,
+ },
mixins: [
TableRowExpandMixin,
DataFormatterMixin,
@@ -211,7 +222,9 @@
created() {
this.$store.dispatch('powerSupply/getAllPowerSupplies').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('hardware-status-power-supplies-complete');
+ require('@/eventBus').default.$emit(
+ 'hardware-status-power-supplies-complete',
+ );
this.isBusy = false;
});
},
diff --git a/src/views/HardwareStatus/Inventory/InventoryTableProcessors.vue b/src/views/HardwareStatus/Inventory/InventoryTableProcessors.vue
index 4bdff54..5290133 100644
--- a/src/views/HardwareStatus/Inventory/InventoryTableProcessors.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryTableProcessors.vue
@@ -17,13 +17,15 @@
</b-row>
<b-table
sort-icon-left
- no-sort-reset
+ must-sort
hover
responsive="md"
+ thead-class="table-light"
+ :sort-by="['health']"
show-empty
:items="processors"
:fields="fields"
- :sort-desc="true"
+ :sort-desc="[true]"
:filter="searchFilter"
:empty-text="$t('global.table.emptyMessage')"
:empty-filtered-text="$t('global.table.emptySearchMessage')"
@@ -37,10 +39,11 @@
data-test-id="hardwareStatus-button-expandProcessors"
:title="expandRowLabel"
class="btn-icon-only"
+ :class="{ collapsed: !row.detailsShowing }"
@click="toggleRowDetails(row)"
>
<icon-chevron />
- <span class="sr-only">{{ expandRowLabel }}</span>
+ <span class="visually-hidden">{{ expandRowLabel }}</span>
</b-button>
</template>
<!-- Health -->
@@ -87,7 +90,9 @@
<dd>{{ dataFormatter(item.serialNumber) }}</dd>
<!-- Spare Part Number -->
<dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt>
- <dd>{{ dataFormatter(item.sparePartNumber) }}</dd>
+ <dd>
+ {{ dataFormatter(item.sparePartNumber) }}
+ </dd>
<!-- Model -->
<dt>{{ $t('pageInventory.table.model') }}:</dt>
<dd>{{ dataFormatter(item.model) }}</dd>
@@ -119,10 +124,14 @@
<dd>{{ dataFormatter(item.processorType) }}</dd>
<!-- Processor Architecture -->
<dt>{{ $t('pageInventory.table.processorArchitecture') }}:</dt>
- <dd>{{ dataFormatter(item.processorArchitecture) }}</dd>
+ <dd>
+ {{ dataFormatter(item.processorArchitecture) }}
+ </dd>
<!-- Instruction Set -->
<dt>{{ $t('pageInventory.table.instructionSet') }}:</dt>
- <dd>{{ dataFormatter(item.instructionSet) }}</dd>
+ <dd>
+ {{ dataFormatter(item.instructionSet) }}
+ </dd>
<!-- Version -->
<dt>{{ $t('pageInventory.table.version') }}:</dt>
<dd>{{ dataFormatter(item.version) }}</dd>
@@ -176,7 +185,13 @@
import i18n from '@/i18n';
export default {
- components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount },
+ components: {
+ IconChevron,
+ PageSection,
+ StatusIcon,
+ Search,
+ TableCellCount,
+ },
mixins: [
BVToastMixin,
TableRowExpandMixin,
@@ -246,7 +261,9 @@
created() {
this.$store.dispatch('processors/getProcessorsInfo').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('hardware-status-processors-complete');
+ require('@/eventBus').default.$emit(
+ 'hardware-status-processors-complete',
+ );
this.isBusy = false;
});
},
diff --git a/src/views/HardwareStatus/Inventory/InventoryTableSystem.vue b/src/views/HardwareStatus/Inventory/InventoryTableSystem.vue
index 23beb47..a3cb0cf 100644
--- a/src/views/HardwareStatus/Inventory/InventoryTableSystem.vue
+++ b/src/views/HardwareStatus/Inventory/InventoryTableSystem.vue
@@ -4,6 +4,7 @@
responsive="md"
hover
show-empty
+ thead-class="table-light"
:items="systems"
:fields="fields"
:empty-text="$t('global.table.emptyMessage')"
@@ -16,10 +17,11 @@
data-test-id="hardwareStatus-button-expandSystem"
:title="expandRowLabel"
class="btn-icon-only"
+ :class="{ collapsed: !row.detailsShowing }"
@click="toggleRowDetails(row)"
>
<icon-chevron />
- <span class="sr-only">{{ expandRowLabel }}</span>
+ <span class="visually-hidden">{{ expandRowLabel }}</span>
</b-button>
</template>
@@ -103,7 +105,7 @@
<p class="mt-1 mb-2 h6 float-none m-0">
{{ $t('pageInventory.table.memorySummary') }}
</p>
- <dl class="ml-4">
+ <dl class="ms-4">
<!-- Total system memory -->
<dt>{{ $t('pageInventory.table.totalSystemMemoryGiB') }}:</dt>
<dd>
@@ -115,23 +117,31 @@
<p class="mt-1 mb-2 h6 float-none m-0">
{{ $t('pageInventory.table.processorSummary') }}
</p>
- <dl class="ml-4">
+ <dl class="ms-4">
<!-- Count -->
<dt>{{ $t('pageInventory.table.count') }}:</dt>
- <dd>{{ dataFormatter(item.processorSummaryCount) }}</dd>
+ <dd>
+ {{ dataFormatter(item.processorSummaryCount) }}
+ </dd>
<!-- Core Count -->
<dt>{{ $t('pageInventory.table.coreCount') }}:</dt>
- <dd>{{ dataFormatter(item.processorSummaryCoreCount) }}</dd>
+ <dd>
+ {{ dataFormatter(item.processorSummaryCoreCount) }}
+ </dd>
</dl>
<!-- Serial console -->
<p class="mt-1 mb-2 h6 float-none m-0">
{{ $t('pageInventory.table.serialConsole') }}
</p>
- <dl class="ml-4">
+ <dl class="ms-4">
<dt>{{ $t('pageInventory.table.maxConcurrentSessions') }}:</dt>
- <dd>{{ dataFormatter(item.serialConsoleMaxSessions) }}</dd>
+ <dd>
+ {{ dataFormatter(item.serialConsoleMaxSessions) }}
+ </dd>
<dt>{{ $t('pageInventory.table.serviceEnabled') }}:</dt>
- <dd>{{ dataFormatter(item.serialConsoleEnabled) }}</dd>
+ <dd>
+ {{ dataFormatter(item.serialConsoleEnabled) }}
+ </dd>
</dl>
</b-col>
</b-row>
@@ -207,7 +217,7 @@
created() {
this.$store.dispatch('system/getSystem').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('hardware-status-system-complete');
+ require('@/eventBus').default.$emit('hardware-status-system-complete');
this.isBusy = false;
});
},
diff --git a/src/views/HardwareStatus/Sensors/Sensors.vue b/src/views/HardwareStatus/Sensors/Sensors.vue
index ac70e40..120cf66 100644
--- a/src/views/HardwareStatus/Sensors/Sensors.vue
+++ b/src/views/HardwareStatus/Sensors/Sensors.vue
@@ -16,7 +16,7 @@
:total-number-of-cells="allSensors.length"
></table-cell-count>
</b-col>
- <b-col sm="3" md="4" xl="6" class="text-right">
+ <b-col sm="3" md="4" xl="6" class="text-end">
<table-filter :filters="tableFilters" @filter-change="onFilterChange" />
</b-col>
</b-row>
@@ -24,7 +24,9 @@
<b-col xl="12">
<table-toolbar
ref="toolbar"
- :selected-items-count="selectedRows.length"
+ :selected-items-count="
+ Array.isArray(selectedRows) ? selectedRows.length : 0
+ "
@clear-selected="clearSelectedRows($refs.table)"
>
<template #toolbar-buttons>
@@ -41,15 +43,14 @@
no-select-on-click
sort-icon-left
hover
- no-sort-reset
+ must-sort
sticky-header="75vh"
- sort-by="status"
+ thead-class="table-light"
+ :sort-by="['status']"
show-empty
- :no-border-collapse="true"
:items="filteredSensors"
:fields="fields"
- :sort-desc="true"
- :sort-compare="sortCompare"
+ :sort-desc="[true]"
:filter="searchFilter"
:empty-text="$t('global.table.emptyMessage')"
:empty-filtered-text="$t('global.table.emptySearchMessage')"
@@ -62,9 +63,11 @@
<b-form-checkbox
v-model="tableHeaderCheckboxModel"
:indeterminate="tableHeaderCheckboxIndeterminate"
- @change="onChangeHeaderCheckbox($refs.table)"
+ @change="onChangeHeaderCheckbox($refs.table, $event)"
>
- <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectAll') }}
+ </span>
</b-form-checkbox>
</template>
<template #cell(checkbox)="row">
@@ -72,7 +75,9 @@
v-model="row.rowSelected"
@change="toggleSelectRow($refs.table, row.index)"
>
- <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectItem') }}
+ </span>
</b-form-checkbox>
</template>
@@ -235,11 +240,6 @@
});
},
methods: {
- sortCompare(a, b, key) {
- if (key === 'status') {
- return this.sortStatus(a, b, key);
- }
- },
onFilterChange({ activeFilters }) {
this.activeFilters = activeFilters;
},
diff --git a/src/views/Login/Login.vue b/src/views/Login/Login.vue
index 5212f92..028d80c 100644
--- a/src/views/Login/Login.vue
+++ b/src/views/Login/Login.vue
@@ -20,6 +20,7 @@
aria-describedby="login-error-alert username-required"
:state="getValidationState(v$.userInfo.username)"
type="text"
+ autocomplete="username"
autofocus="autofocus"
data-test-id="login-input-username"
@input="v$.userInfo.username.$touch()"
@@ -32,7 +33,9 @@
</b-form-invalid-feedback>
</b-form-group>
<div class="login-form__section mb-3">
- <label for="password">{{ $t('pageLogin.password') }}</label>
+ <label for="password" class="d-block">
+ {{ $t('pageLogin.password') }}
+ </label>
<input-password-toggle>
<b-form-input
id="password"
@@ -40,6 +43,7 @@
aria-describedby="login-error-alert password-required"
:state="getValidationState(v$.userInfo.password)"
type="password"
+ autocomplete="current-password"
data-test-id="login-input-password"
class="form-control-with-button"
@input="v$.userInfo.password.$touch()"
@@ -117,15 +121,17 @@
return this.$store.getters['authentication/authError'];
},
},
- validations: {
- userInfo: {
- username: {
- required,
+ validations() {
+ return {
+ userInfo: {
+ username: {
+ required,
+ },
+ password: {
+ required,
+ },
},
- password: {
- required,
- },
- },
+ };
},
methods: {
login: function () {
diff --git a/src/views/Logs/Dumps/Dumps.vue b/src/views/Logs/Dumps/Dumps.vue
index 4783f82..d500c36 100644
--- a/src/views/Logs/Dumps/Dumps.vue
+++ b/src/views/Logs/Dumps/Dumps.vue
@@ -18,7 +18,7 @@
@change-search="onChangeSearchInput"
@clear-search="onClearSearchInput"
/>
- <div class="ml-sm-4">
+ <div class="ms-sm-4">
<table-cell-count
:filtered-items-count="filteredRows"
:total-number-of-cells="allDumps.length"
@@ -30,7 +30,7 @@
</b-col>
</b-row>
<b-row>
- <b-col class="text-right">
+ <b-col class="text-end">
<table-filter
:filters="tableFilters"
@filter-change="onFilterChange"
@@ -38,7 +38,9 @@
</b-col>
</b-row>
<table-toolbar
- :selected-items-count="selectedRows.length"
+ :selected-items-count="
+ Array.isArray(selectedRows) ? selectedRows.length : 0
+ "
:actions="batchActions"
@clear-selected="clearSelectedRows($refs.table)"
@batch-action="onTableBatchAction"
@@ -48,12 +50,13 @@
show-empty
hover
sort-icon-left
- no-sort-reset
- sort-desc
+ must-sort
+ thead-class="table-light"
+ :sort-desc="[true]"
selectable
no-select-on-click
responsive="md"
- sort-by="dateTime"
+ :sort-by="['dateTime']"
:fields="fields"
:items="filteredDumps"
:empty-text="$t('global.table.emptyMessage')"
@@ -61,16 +64,18 @@
:filter="searchFilter"
:busy="isBusy"
@filtered="onFiltered"
- @row-selected="onRowSelected($event, filteredTableItems.length)"
+ @row-selected="onRowSelected($event, filteredRows)"
>
<!-- Checkbox column -->
<template #head(checkbox)>
<b-form-checkbox
v-model="tableHeaderCheckboxModel"
:indeterminate="tableHeaderCheckboxIndeterminate"
- @change="onChangeHeaderCheckbox($refs.table)"
+ @change="onChangeHeaderCheckbox($refs.table, $event)"
>
- <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectAll') }}
+ </span>
</b-form-checkbox>
</template>
<template #cell(checkbox)="row">
@@ -78,7 +83,9 @@
v-model="row.rowSelected"
@change="toggleSelectRow($refs.table, row.index)"
>
- <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectItem') }}
+ </span>
</b-form-checkbox>
</template>
@@ -135,7 +142,7 @@
first-number
last-number
:per-page="perPage"
- :total-rows="getTotalRowCount()"
+ :total-rows="getTotalRowCount(filteredRows)"
aria-controls="table-dump-entries"
/>
</b-col>
@@ -173,6 +180,7 @@
import TableFilterMixin from '@/components/Mixins/TableFilterMixin';
import i18n from '@/i18n';
import { useI18n } from 'vue-i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
components: {
@@ -202,6 +210,10 @@
this.hideLoader();
next();
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
@@ -235,7 +247,7 @@
key: 'actions',
sortable: false,
label: '',
- tdClass: 'text-right text-nowrap',
+ tdClass: 'text-end text-nowrap',
},
],
batchActions: [
@@ -328,78 +340,60 @@
this.filterStartDate = fromDate;
this.filterEndDate = toDate;
},
- onTableRowAction(action, dump) {
+ async onTableRowAction(action, item) {
if (action === 'delete') {
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t('pageDumps.modal.deleteDumpConfirmation'),
- {
- title: i18n.global.t('pageDumps.modal.deleteDump'),
- okTitle: i18n.global.t('pageDumps.modal.deleteDump'),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfrimed) => {
- if (deleteConfrimed) {
- this.$store
- .dispatch('dumps/deleteDumps', [dump])
- .then((messages) => {
- messages.forEach(({ type, message }) => {
- if (type === 'success') {
- this.successToast(message);
- } else if (type === 'error') {
- this.errorToast(message);
- }
- });
- });
- }
+ const ok = await this.confirmDialog(
+ i18n.global.t('pageDumps.modal.deleteDumpConfirmation', 1),
+ {
+ title: i18n.global.t('pageDumps.modal.deleteDump', 1),
+ okTitle: i18n.global.t('pageDumps.modal.deleteDump', 1),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ },
+ );
+ if (ok)
+ this.$store.dispatch('dumps/deleteDumps', [item]).then((messages) => {
+ messages.forEach(({ type, message }) => {
+ if (type === 'success') {
+ this.successToast(message);
+ } else if (type === 'error') {
+ this.errorToast(message);
+ }
+ });
});
}
},
- onTableBatchAction(action) {
+ async onTableBatchAction(action) {
if (action === 'delete') {
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t(
- 'pageDumps.modal.deleteDumpConfirmation',
- this.selectedRows.length,
- ),
- {
- title: i18n.global.t(
- 'pageDumps.modal.deleteDump',
- this.selectedRows.length,
- ),
- okTitle: i18n.global.t(
- 'pageDumps.modal.deleteDump',
- this.selectedRows.length,
- ),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfrimed) => {
- if (deleteConfrimed) {
- if (this.selectedRows.length === this.dumps.length) {
- this.$store
- .dispatch('dumps/deleteAllDumps')
- .then((success) => this.successToast(success))
- .catch(({ message }) => this.errorToast(message));
- } else {
- this.$store
- .dispatch('dumps/deleteDumps', this.selectedRows)
- .then((messages) => {
- messages.forEach(({ type, message }) => {
- if (type === 'success') {
- this.successToast(message);
- } else if (type === 'error') {
- this.errorToast(message);
- }
- });
- });
- }
- }
- });
+ const ids = (this.selectedRows || []).map((r) => r.Id);
+ const count = ids.length;
+ const ok = await this.confirmDialog(
+ i18n.global.t('pageDumps.modal.deleteDumpConfirmation', count),
+ {
+ title: i18n.global.t('pageDumps.modal.deleteDump', count),
+ okTitle: i18n.global.t('pageDumps.modal.deleteDump', count),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ },
+ );
+ if (ok) {
+ if (this.selectedRows.length === this.dumps.length) {
+ this.$store
+ .dispatch('dumps/deleteAllDumps')
+ .then((success) => this.successToast(success))
+ .catch(({ message }) => this.errorToast(message));
+ } else {
+ this.$store
+ .dispatch('dumps/deleteDumps', this.selectedRows)
+ .then((messages) => {
+ messages.forEach(({ type, message }) => {
+ if (type === 'success') {
+ this.successToast(message);
+ } else if (type === 'error') {
+ this.errorToast(message);
+ }
+ });
+ });
+ }
+ }
}
},
exportFileName(row) {
@@ -407,6 +401,9 @@
filename = filename.replace(RegExp(' ', 'g'), '_');
return filename;
},
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
+ },
},
};
</script>
diff --git a/src/views/Logs/Dumps/DumpsForm.vue b/src/views/Logs/Dumps/DumpsForm.vue
index b27aeba..af9df83 100644
--- a/src/views/Logs/Dumps/DumpsForm.vue
+++ b/src/views/Logs/Dumps/DumpsForm.vue
@@ -24,14 +24,16 @@
<alert variant="info" class="mb-3" :show="selectedDumpType === 'system'">
{{ $t('pageDumps.form.systemDumpInfo') }}
</alert>
- <b-button variant="primary" type="submit" form="form-new-dump">
+ <b-button
+ variant="primary"
+ type="submit"
+ form="form-new-dump"
+ class="mt-3"
+ >
{{ $t('pageDumps.form.initiateDump') }}
</b-button>
</b-form>
- <modal-confirmation
- :require-confirmation="selectedDumpType === 'system'"
- @ok="createSystemDump"
- />
+ <modal-confirmation v-model="showConfirmation" @ok="createSystemDump" />
</div>
</template>
@@ -44,19 +46,23 @@
import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
import i18n from '@/i18n';
import { useI18n } from 'vue-i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
components: { Alert, ModalConfirmation },
mixins: [BVToastMixin, VuelidateMixin],
setup() {
+ const bvModal = useModal();
return {
v$: useVuelidate(),
+ bvModal,
};
},
data() {
return {
$t: useI18n().t,
selectedDumpType: null,
+ showConfirmation: false,
dumpTypeOptions: [
{ value: 'bmc', text: i18n.global.t('pageDumps.dumpTypes.bmcDump') },
{
@@ -99,7 +105,7 @@
}
},
showConfirmationModal() {
- this.$bvModal.show('modal-confirmation');
+ this.showConfirmation = true;
},
createSystemDump() {
this.$store
diff --git a/src/views/Logs/Dumps/DumpsModalConfirmation.vue b/src/views/Logs/Dumps/DumpsModalConfirmation.vue
index 77f03b0..466f6ce 100644
--- a/src/views/Logs/Dumps/DumpsModalConfirmation.vue
+++ b/src/views/Logs/Dumps/DumpsModalConfirmation.vue
@@ -26,7 +26,7 @@
>
{{ $t('global.form.required') }}
</b-form-invalid-feedback>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button variant="secondary" @click="cancel()">
{{ $t('global.action.cancel') }}
</b-button>
diff --git a/src/views/Logs/EventLogs/EventLogs.vue b/src/views/Logs/EventLogs/EventLogs.vue
index 392125c..e149739 100644
--- a/src/views/Logs/EventLogs/EventLogs.vue
+++ b/src/views/Logs/EventLogs/EventLogs.vue
@@ -9,7 +9,7 @@
@change-search="onChangeSearchInput"
@clear-search="onClearSearchInput"
/>
- <div class="ml-sm-4">
+ <div class="ms-sm-4">
<table-cell-count
:filtered-items-count="filteredRows"
:total-number-of-cells="allLogs.length"
@@ -21,7 +21,7 @@
</b-col>
</b-row>
<b-row>
- <b-col class="text-right">
+ <b-col class="text-end">
<table-filter :filters="tableFilters" @filter-change="onFilterChange" />
<b-button
variant="link"
@@ -74,13 +74,13 @@
no-select-on-click
sort-icon-left
hover
- no-sort-reset
- sort-desc
+ must-sort
+ thead-class="table-light"
+ :sort-desc="[true]"
show-empty
- sort-by="id"
+ :sort-by="['id']"
:fields="fields"
:items="filteredLogs"
- :sort-compare="onSortCompare"
:empty-text="$t('global.table.emptyMessage')"
:empty-filtered-text="$t('global.table.emptySearchMessage')"
:per-page="perPage"
@@ -96,9 +96,11 @@
v-model="tableHeaderCheckboxModel"
data-test-id="eventLogs-checkbox-selectAll"
:indeterminate="tableHeaderCheckboxIndeterminate"
- @change="onChangeHeaderCheckbox($refs.table)"
+ @change="onChangeHeaderCheckbox($refs.table, $event)"
>
- <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectAll') }}
+ </span>
</b-form-checkbox>
</template>
<template #cell(checkbox)="row">
@@ -107,7 +109,9 @@
:data-test-id="`eventLogs-checkbox-selectRow-${row.index}`"
@change="toggleSelectRow($refs.table, row.index)"
>
- <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectItem') }}
+ </span>
</b-form-checkbox>
</template>
@@ -120,7 +124,8 @@
class="btn-icon-only"
@click="toggleRowDetails(row)"
>
- <icon-chevron />
+ <icon-chevron v-if="!row.detailsShowing" />
+ <icon-chevron-up v-else />
</b-button>
</template>
@@ -181,7 +186,9 @@
<span v-if="row.item.status">
{{ $t('pageEventLogs.resolved') }}
</span>
- <span v-else> {{ $t('pageEventLogs.unresolved') }} </span>
+ <span v-else>
+ {{ $t('pageEventLogs.unresolved') }}
+ </span>
</b-form-checkbox>
</template>
<template #cell(filterByStatus)="{ value }">
@@ -244,6 +251,7 @@
import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
import IconExport from '@carbon/icons-vue/es/document--export/20';
import IconChevron from '@carbon/icons-vue/es/chevron--down/20';
+import IconChevronUp from '@carbon/icons-vue/es/chevron--up/20';
import IconDownload from '@carbon/icons-vue/es/download/20';
import { omit } from 'lodash';
@@ -280,6 +288,7 @@
} from '@/components/Mixins/SearchFilterMixin';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
components: {
@@ -287,6 +296,7 @@
IconExport,
IconTrashcan,
IconChevron,
+ IconChevronUp,
IconDownload,
PageTitle,
Search,
@@ -315,6 +325,10 @@
this.hideLoader();
next();
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
@@ -361,7 +375,7 @@
key: 'actions',
sortable: false,
label: '',
- tdClass: 'text-right text-nowrap',
+ tdClass: 'text-end text-nowrap',
},
],
tableFilters:
@@ -495,23 +509,23 @@
})
.catch(({ message }) => this.errorToast(message));
},
- deleteAllLogs() {
- this.$bvModal
- .msgBoxConfirm(i18n.global.t('pageEventLogs.modal.deleteAllMessage'), {
+ async deleteAllLogs() {
+ const ok = await this.confirmDialog(
+ i18n.global.t('pageEventLogs.modal.deleteAllMessage'),
+ {
title: i18n.global.t('pageEventLogs.modal.deleteAllTitle'),
okTitle: i18n.global.t('global.action.delete'),
okVariant: 'danger',
cancelTitle: i18n.global.t('global.action.cancel'),
autoFocusButton: 'cancel',
- })
- .then((deleteConfirmed) => {
- if (deleteConfirmed) {
- this.$store
- .dispatch('eventLog/deleteAllEventLogs', this.allLogs)
- .then((message) => this.successToast(message))
- .catch(({ message }) => this.errorToast(message));
- }
- });
+ },
+ );
+ if (ok) {
+ this.$store
+ .dispatch('eventLog/deleteAllEventLogs', this.allLogs)
+ .then((message) => this.successToast(message))
+ .catch(({ message }) => this.errorToast(message));
+ }
},
deleteLogs(uris) {
this.$store
@@ -537,66 +551,48 @@
onFilterChange({ activeFilters }) {
this.activeFilters = activeFilters;
},
- onSortCompare(a, b, key) {
- if (key === 'severity') {
- return this.sortStatus(a, b, key);
- }
- },
onTableRowAction(action, { uri }) {
if (action === 'delete') {
- this.$bvModal
- .msgBoxConfirm(i18n.global.t('pageEventLogs.modal.deleteMessage'), {
- title: i18n.global.t('pageEventLogs.modal.deleteTitle'),
+ this.confirmDialog(i18n.global.t('pageEventLogs.modal.deleteMessage'), {
+ title: i18n.global.t('pageEventLogs.modal.deleteTitle'),
+ okTitle: i18n.global.t('global.action.delete'),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ }).then((deleteConfirmed) => {
+ if (deleteConfirmed) this.deleteLogs([uri]);
+ });
+ }
+ },
+ async onBatchAction(action) {
+ if (action === 'delete') {
+ const uris = this.selectedRows.map((row) => row.uri);
+ const count = this.selectedRows.length;
+ const ok = await this.confirmDialog(
+ i18n.global.t('pageEventLogs.modal.deleteMessage', count),
+ {
+ title: i18n.global.t('pageEventLogs.modal.deleteTitle', count),
okTitle: i18n.global.t('global.action.delete'),
cancelTitle: i18n.global.t('global.action.cancel'),
autoFocusButton: 'ok',
- })
- .then((deleteConfirmed) => {
- if (deleteConfirmed) this.deleteLogs([uri]);
- });
- }
- },
- onBatchAction(action) {
- if (action === 'delete') {
- const uris = this.selectedRows.map((row) => row.uri);
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t(
- 'pageEventLogs.modal.deleteMessage',
- this.selectedRows.length,
- ),
- {
- title: i18n.global.t(
- 'pageEventLogs.modal.deleteTitle',
- this.selectedRows.length,
- ),
- okTitle: i18n.global.t('global.action.delete'),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfirmed) => {
- if (deleteConfirmed) {
- if (this.selectedRows.length === this.allLogs.length) {
- this.$store
- .dispatch(
- 'eventLog/deleteAllEventLogs',
- this.selectedRows.length,
- )
- .then(() => {
- this.successToast(
- i18n.global.t(
- 'pageEventLogs.toast.successDelete',
- uris.length,
- ),
- );
- })
- .catch(({ message }) => this.errorToast(message));
- } else {
- this.deleteLogs(uris);
- }
- }
- });
+ },
+ );
+ if (ok) {
+ if (this.selectedRows.length === this.allLogs.length) {
+ this.$store
+ .dispatch('eventLog/deleteAllEventLogs', this.selectedRows.length)
+ .then(() => {
+ this.successToast(
+ i18n.global.t(
+ 'pageEventLogs.toast.successDelete',
+ uris.length,
+ ),
+ );
+ })
+ .catch(({ message }) => this.errorToast(message));
+ } else {
+ this.deleteLogs(uris);
+ }
+ }
}
},
onChangeDateTimeFilter({ fromDate, toDate }) {
@@ -647,6 +643,9 @@
});
});
},
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
+ },
},
};
</script>
diff --git a/src/views/Logs/PostCodeLogs/PostCodeLogs.vue b/src/views/Logs/PostCodeLogs/PostCodeLogs.vue
index 6d8ff90..62caccf 100644
--- a/src/views/Logs/PostCodeLogs/PostCodeLogs.vue
+++ b/src/views/Logs/PostCodeLogs/PostCodeLogs.vue
@@ -8,7 +8,7 @@
@change-search="onChangeSearchInput"
@clear-search="onClearSearchInput"
/>
- <div class="ml-sm-4">
+ <div class="ms-sm-4">
<table-cell-count
:filtered-items-count="filteredRows"
:total-number-of-cells="allLogs.length"
@@ -20,7 +20,7 @@
</b-col>
</b-row>
<b-row>
- <b-col xl="12" class="text-right">
+ <b-col xl="12" class="text-end">
<b-button
variant="link"
:disabled="allLogs.length === 0"
@@ -34,7 +34,8 @@
:download="exportFileNameByDate()"
:href="href"
>
- <icon-export /> {{ $t('pagePostCodeLogs.button.exportAll') }}
+ <icon-export />
+ {{ $t('pagePostCodeLogs.button.exportAll') }}
</b-button>
</b-col>
</b-row>
@@ -42,7 +43,9 @@
<b-col>
<table-toolbar
ref="toolbar"
- :selected-items-count="selectedRows.length"
+ :selected-items-count="
+ Array.isArray(selectedRows) ? selectedRows.length : 0
+ "
@clear-selected="clearSelectedRows($refs.table)"
>
<template #toolbar-buttons>
@@ -60,10 +63,11 @@
no-select-on-click
sort-icon-left
hover
- no-sort-reset
- sort-desc
+ must-sort
+ thead-class="table-light"
+ :sort-desc="[true]"
show-empty
- sort-by="id"
+ :sort-by="['id']"
:fields="fields"
:items="filteredLogs"
:empty-text="$t('global.table.emptyMessage')"
@@ -81,9 +85,11 @@
v-model="tableHeaderCheckboxModel"
data-test-id="postCode-checkbox-selectAll"
:indeterminate="tableHeaderCheckboxIndeterminate"
- @change="onChangeHeaderCheckbox($refs.table)"
+ @change="onChangeHeaderCheckbox($refs.table, $event)"
>
- <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectAll') }}
+ </span>
</b-form-checkbox>
</template>
<template #cell(checkbox)="row">
@@ -92,7 +98,9 @@
:data-test-id="`postCode-checkbox-selectRow-${row.index}`"
@change="toggleSelectRow($refs.table, row.index)"
>
- <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectItem') }}
+ </span>
</b-form-checkbox>
</template>
<!-- Date column -->
@@ -111,9 +119,12 @@
:row-data="row.item"
:btn-icon-only="true"
:export-name="exportFileNameByDate(action.value)"
- :download-location="row.item.uri"
- :download-in-new-tab="true"
- :show-button="false"
+ :download-location="
+ action.value === 'download' ? '' : row.item.uri
+ "
+ :download-in-new-tab="false"
+ :show-button="action.value === 'download' ? true : false"
+ @click-table-action="onRowAction(action.value, row.item)"
>
<template #icon>
<icon-export v-if="action.value === 'export'" />
@@ -188,6 +199,7 @@
} from '@/components/Mixins/SearchFilterMixin';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
components: {
@@ -218,6 +230,10 @@
this.hideLoader();
next();
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
@@ -247,7 +263,7 @@
{
key: 'actions',
label: '',
- tdClass: 'text-right text-nowrap',
+ tdClass: 'text-end text-nowrap',
},
],
expandRowLabel,
@@ -317,23 +333,37 @@
});
},
methods: {
- deleteAllLogs() {
- this.$bvModal
- .msgBoxConfirm(i18n.global.t('pageEventLogs.modal.deleteAllMessage'), {
+ onRowAction(action, item) {
+ if (action === 'download') {
+ this.$store
+ .dispatch('postCodeLogs/downloadEntry', item.uri)
+ .then((blob) => {
+ const link = document.createElement('a');
+ link.href = URL.createObjectURL(blob);
+ link.download = this.exportFileNameByDate('download');
+ link.click();
+ URL.revokeObjectURL(link.href);
+ })
+ .catch(({ message }) => this.errorToast(message));
+ }
+ },
+ async deleteAllLogs() {
+ const deleteConfirmed = await this.confirmDialog(
+ i18n.global.t('pageEventLogs.modal.deleteAllMessage'),
+ {
title: i18n.global.t('pageEventLogs.modal.deleteAllTitle'),
okTitle: i18n.global.t('global.action.delete'),
okVariant: 'danger',
cancelTitle: i18n.global.t('global.action.cancel'),
autoFocusButton: 'cancel',
- })
- .then((deleteConfirmed) => {
- if (deleteConfirmed) {
- this.$store
- .dispatch('postCodeLogs/deleteAllPostCodeLogs', this.allLogs)
- .then((message) => this.successToast(message))
- .catch(({ message }) => this.errorToast(message));
- }
- });
+ },
+ );
+ if (deleteConfirmed) {
+ this.$store
+ .dispatch('postCodeLogs/deleteAllPostCodeLogs', this.allLogs)
+ .then((message) => this.successToast(message))
+ .catch(({ message }) => this.errorToast(message));
+ }
},
exportAllLogsString() {
{
@@ -372,6 +402,9 @@
}
return fileName + date;
},
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
+ },
},
};
</script>
diff --git a/src/views/Operations/FactoryReset/FactoryReset.vue b/src/views/Operations/FactoryReset/FactoryReset.vue
index f59b0a2..8e9ed8f 100644
--- a/src/views/Operations/FactoryReset/FactoryReset.vue
+++ b/src/views/Operations/FactoryReset/FactoryReset.vue
@@ -20,7 +20,7 @@
>
{{ $t('pageFactoryReset.form.resetBiosOptionLabel') }}
</b-form-radio>
- <b-form-text id="reset-bios" class="ml-4 mb-3">
+ <b-form-text id="reset-bios" class="ms-4 mb-3">
{{ $t('pageFactoryReset.form.resetBiosOptionHelperText') }}
</b-form-text>
@@ -32,7 +32,7 @@
>
{{ $t('pageFactoryReset.form.resetToDefaultsOptionLabel') }}
</b-form-radio>
- <b-form-text id="reset-to-defaults" class="ml-4 mb-3">
+ <b-form-text id="reset-to-defaults" class="ms-4 mb-3">
{{
$t('pageFactoryReset.form.resetToDefaultsOptionHelperText')
}}
@@ -51,7 +51,11 @@
</b-form>
<!-- Modals -->
- <modal-reset :reset-type="resetOption" @ok-confirm="onOkConfirm" />
+ <modal-reset
+ v-model="showResetModal"
+ :reset-type="resetOption"
+ @ok-confirm="onOkConfirm"
+ />
</b-container>
</template>
@@ -69,6 +73,7 @@
data() {
return {
$t: useI18n().t,
+ showResetModal: false,
resetOption: 'resetBios',
};
},
@@ -77,7 +82,7 @@
},
methods: {
onResetSubmit() {
- this.$bvModal.show('modal-reset');
+ this.showResetModal = true;
},
onOkConfirm() {
if (this.resetOption == 'resetBios') {
diff --git a/src/views/Operations/FactoryReset/FactoryResetModal.vue b/src/views/Operations/FactoryReset/FactoryResetModal.vue
index 5865bb3..97c74c4 100644
--- a/src/views/Operations/FactoryReset/FactoryResetModal.vue
+++ b/src/views/Operations/FactoryReset/FactoryResetModal.vue
@@ -2,33 +2,33 @@
<b-modal
id="modal-reset"
ref="modal"
- :title="$t(`pageFactoryReset.modal.${resetType}Title`)"
+ :title="modalTitle"
title-tag="h2"
@hidden="resetConfirm"
>
<p class="mb-2">
- <strong>{{ $t(`pageFactoryReset.modal.${resetType}Header`) }}</strong>
+ <strong>{{ modalHeader }}</strong>
</p>
- <ul v-if="resetType == 'resetBios'" class="pl-3 mb-4">
+ <ul v-if="resetType == 'resetBios'" class="ps-3 mb-4">
<li class="mt-1 mb-1">
- {{ $t('pageFactoryReset.modal.resetBiosSettingsList.item1') }}
+ {{ t('pageFactoryReset.modal.resetBiosSettingsList.item1') }}
</li>
<li class="mt-1 mb-1">
- {{ $t('pageFactoryReset.modal.resetBiosSettingsList.item2') }}
+ {{ t('pageFactoryReset.modal.resetBiosSettingsList.item2') }}
</li>
</ul>
- <ul v-else-if="resetType == 'resetToDefaults'" class="pl-3 mb-4">
+ <ul v-else-if="resetType == 'resetToDefaults'" class="ps-3 mb-4">
<li class="mt-1 mb-1">
- {{ $t('pageFactoryReset.modal.resetToDefaultsSettingsList.item1') }}
+ {{ t('pageFactoryReset.modal.resetToDefaultsSettingsList.item1') }}
</li>
<li class="mt-1 mb-1">
- {{ $t('pageFactoryReset.modal.resetToDefaultsSettingsList.item2') }}
+ {{ t('pageFactoryReset.modal.resetToDefaultsSettingsList.item2') }}
</li>
<li class="mt-1 mb-1">
- {{ $t('pageFactoryReset.modal.resetToDefaultsSettingsList.item3') }}
+ {{ t('pageFactoryReset.modal.resetToDefaultsSettingsList.item3') }}
</li>
<li class="mt-1 mb-1">
- {{ $t('pageFactoryReset.modal.resetToDefaultsSettingsList.item4') }}
+ {{ t('pageFactoryReset.modal.resetToDefaultsSettingsList.item4') }}
</li>
</ul>
@@ -36,8 +36,8 @@
<template v-if="!isServerOff">
<p class="d-flex mb-2">
<status-icon status="danger" />
- <span id="reset-to-default-warning" class="ml-1">
- {{ $t(`pageFactoryReset.modal.resetWarningMessage`) }}
+ <span id="reset-to-default-warning" class="ms-1">
+ {{ t(`pageFactoryReset.modal.resetWarningMessage`) }}
</span>
</p>
<b-form-checkbox
@@ -45,23 +45,23 @@
aria-describedby="reset-to-default-warning"
@input="v$.confirm.$touch()"
>
- {{ $t(`pageFactoryReset.modal.resetWarningCheckLabel`) }}
+ {{ t(`pageFactoryReset.modal.resetWarningCheckLabel`) }}
</b-form-checkbox>
<b-form-invalid-feedback
role="alert"
:state="getValidationState(v$.confirm)"
>
- {{ $t('global.form.fieldRequired') }}
+ {{ t('global.form.fieldRequired') }}
</b-form-invalid-feedback>
</template>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button
variant="secondary"
data-test-id="factoryReset-button-cancel"
@click="cancel()"
>
- {{ $t('global.action.cancel') }}
+ {{ t('global.action.cancel') }}
</b-button>
<b-button
type="sumbit"
@@ -69,7 +69,7 @@
data-test-id="factoryReset-button-confirm"
@click="handleConfirm"
>
- {{ $t(`pageFactoryReset.modal.${resetType}SubmitText`) }}
+ {{ modalSubmitText }}
</b-button>
</template>
</b-modal>
@@ -97,7 +97,7 @@
},
data() {
return {
- $t: useI18n().t,
+ t: useI18n().t,
confirm: false,
};
},
@@ -108,6 +108,15 @@
isServerOff() {
return this.serverStatus === 'off' ? true : false;
},
+ modalTitle() {
+ return this.t(`pageFactoryReset.modal.${this.resetType}Title`);
+ },
+ modalHeader() {
+ return this.t(`pageFactoryReset.modal.${this.resetType}Header`);
+ },
+ modalSubmitText() {
+ return this.t(`pageFactoryReset.modal.${this.resetType}SubmitText`);
+ },
},
validations: {
confirm: {
@@ -116,6 +125,19 @@
},
},
},
+ watch: {
+ isServerOff: {
+ handler(newValue) {
+ // Touch validation when server is on to show required message immediately
+ if (!newValue) {
+ this.$nextTick(() => {
+ this.v$.confirm.$touch();
+ });
+ }
+ },
+ immediate: true,
+ },
+ },
methods: {
handleConfirm() {
this.v$.$touch();
diff --git a/src/views/Operations/Firmware/FirmwareCardsBios.vue b/src/views/Operations/Firmware/FirmwareCardsBios.vue
index a2994cc..184f0a3 100644
--- a/src/views/Operations/Firmware/FirmwareCardsBios.vue
+++ b/src/views/Operations/Firmware/FirmwareCardsBios.vue
@@ -1,38 +1,45 @@
<template>
<page-section :section-title="$t('pageFirmware.sectionTitleBiosCards')">
- <b-card-group deck>
+ <b-row class="row-cols-1 row-cols-md-2">
<!-- Running image -->
- <b-card>
- <template #header>
- <p class="font-weight-bold m-0">
- {{ $t('pageFirmware.cardTitleRunning') }}
- </p>
- </template>
- <dl class="mb-0">
- <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
- <dd class="mb-0">{{ runningVersion }}</dd>
- </dl>
- </b-card>
+ <b-col class="mb-3">
+ <b-card class="h-100">
+ <template #header>
+ <p class="fw-bold m-0">
+ {{ $t('pageFirmware.cardTitleRunning') }}
+ </p>
+ </template>
+ <dl class="mb-0">
+ <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
+ <dd class="mb-0">{{ runningVersion }}</dd>
+ </dl>
+ </b-card>
+ </b-col>
<!-- Backup image -->
- <b-card>
- <template #header>
- <p class="font-weight-bold m-0">
- {{ $t('pageFirmware.cardTitleBackup') }}
- </p>
- </template>
- <dl class="mb-0">
- <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
- <dd class="mb-0">
- <status-icon v-if="showBackupImageStatus" status="danger" />
- <span v-if="showBackupImageStatus" class="sr-only">
- {{ backupStatus }}
- </span>
- {{ backupVersion }}
- </dd>
- </dl>
- </b-card>
- </b-card-group>
+ <b-col class="mb-3">
+ <b-card class="h-100">
+ <template #header>
+ <p class="fw-bold m-0">
+ {{ $t('pageFirmware.cardTitleBackup') }}
+ </p>
+ </template>
+ <dl class="mb-0">
+ <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
+ <dd class="mb-0">
+ <status-icon v-if="showBackupImageStatus" status="danger" />
+ <span
+ v-if="showBackupImageStatus"
+ class="visually-hidden-focusable"
+ >
+ {{ backupStatus }}
+ </span>
+ {{ backupVersion }}
+ </dd>
+ </dl>
+ </b-card>
+ </b-col>
+ </b-row>
</page-section>
</template>
diff --git a/src/views/Operations/Firmware/FirmwareCardsBmc.vue b/src/views/Operations/Firmware/FirmwareCardsBmc.vue
index 2d18d5b..0e763f3 100644
--- a/src/views/Operations/Firmware/FirmwareCardsBmc.vue
+++ b/src/views/Operations/Firmware/FirmwareCardsBmc.vue
@@ -1,53 +1,64 @@
<template>
<div>
<page-section :section-title="sectionTitle">
- <b-card-group deck>
+ <b-row class="row-cols-1 row-cols-md-2">
<!-- Running image -->
- <b-card>
- <template #header>
- <p class="font-weight-bold m-0">
- {{ $t('pageFirmware.cardTitleRunning') }}
- </p>
- </template>
- <dl class="mb-0">
- <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
- <dd class="mb-0">{{ runningVersion }}</dd>
- </dl>
- </b-card>
+ <b-col class="mb-3">
+ <b-card class="h-100">
+ <template #header>
+ <p class="fw-bold m-0">
+ {{ $t('pageFirmware.cardTitleRunning') }}
+ </p>
+ </template>
+ <dl class="mb-0">
+ <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
+ <dd class="mb-0">{{ runningVersion }}</dd>
+ </dl>
+ </b-card>
+ </b-col>
<!-- Backup image -->
- <b-card>
- <template #header>
- <p class="font-weight-bold m-0">
- {{ $t('pageFirmware.cardTitleBackup') }}
- </p>
- </template>
- <dl>
- <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
- <dd>
- <status-icon v-if="showBackupImageStatus" status="danger" />
- <span v-if="showBackupImageStatus" class="sr-only">
- {{ backupStatus }}
- </span>
- {{ backupVersion }}
- </dd>
- </dl>
- <b-btn
- v-if="!switchToBackupImageDisabled"
- v-b-modal.modal-switch-to-running
- data-test-id="firmware-button-switchToRunning"
- variant="link"
- size="sm"
- class="py-0 px-1 mt-2"
- :disabled="isPageDisabled || !backup || !isServerOff"
- >
- <icon-switch class="d-none d-sm-inline-block" />
- {{ $t('pageFirmware.cardActionSwitchToRunning') }}
- </b-btn>
- </b-card>
- </b-card-group>
+ <b-col class="mb-3">
+ <b-card class="h-100">
+ <template #header>
+ <p class="fw-bold m-0">
+ {{ $t('pageFirmware.cardTitleBackup') }}
+ </p>
+ </template>
+ <dl>
+ <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
+ <dd>
+ <status-icon v-if="showBackupImageStatus" status="danger" />
+ <span
+ v-if="showBackupImageStatus"
+ class="visually-hidden-focusable"
+ >
+ {{ backupStatus }}
+ </span>
+ {{ backupVersion }}
+ </dd>
+ </dl>
+ <b-btn
+ v-if="!switchToBackupImageDisabled"
+ data-test-id="firmware-button-switchToRunning"
+ variant="link"
+ size="sm"
+ class="py-0 px-1 mt-2"
+ :disabled="isPageDisabled || !backup || !isServerOff"
+ @click="showSwitchToRunning = true"
+ >
+ <icon-switch class="d-none d-sm-inline-block" />
+ {{ $t('pageFirmware.cardActionSwitchToRunning') }}
+ </b-btn>
+ </b-card>
+ </b-col>
+ </b-row>
</page-section>
- <modal-switch-to-running :backup="backupVersion" @ok="switchToRunning" />
+ <modal-switch-to-running
+ v-model="showSwitchToRunning"
+ :backup="backupVersion"
+ @ok="switchToRunning"
+ />
</div>
</template>
@@ -82,6 +93,7 @@
loading,
switchToBackupImageDisabled:
process.env.VUE_APP_SWITCH_TO_BACKUP_IMAGE_DISABLED === 'true',
+ showSwitchToRunning: false,
};
},
computed: {
diff --git a/src/views/Operations/Firmware/FirmwareFormUpdate.vue b/src/views/Operations/Firmware/FirmwareFormUpdate.vue
index 2b9a616..a9e327d 100644
--- a/src/views/Operations/Firmware/FirmwareFormUpdate.vue
+++ b/src/views/Operations/Firmware/FirmwareFormUpdate.vue
@@ -6,6 +6,7 @@
<b-form-group
:label="$t('pageFirmware.form.updateFirmware.imageFile')"
label-for="image-file"
+ class="mb-3"
>
<form-file
id="image-file"
@@ -34,7 +35,7 @@
</div>
<!-- Modals -->
- <modal-update-firmware @ok="updateFirmware" />
+ <modal-update-firmware v-model="showUpdateModal" @ok="updateFirmware" />
</div>
</template>
@@ -74,6 +75,7 @@
return {
$t: useI18n().t,
loading,
+ showUpdateModal: false,
file: null,
isServerPowerOffRequired:
process.env.VUE_APP_SERVER_OFF_REQUIRED === 'true',
@@ -120,7 +122,7 @@
onSubmitUpload() {
this.v$.$touch();
if (this.v$.$invalid) return;
- this.$bvModal.show('modal-update-firmware');
+ this.showUpdateModal = true;
},
onFileUpload(file) {
this.file = file;
diff --git a/src/views/Operations/KeyClear/KeyClear.vue b/src/views/Operations/KeyClear/KeyClear.vue
index 7baad34..d0a6a5e 100644
--- a/src/views/Operations/KeyClear/KeyClear.vue
+++ b/src/views/Operations/KeyClear/KeyClear.vue
@@ -4,7 +4,7 @@
<b-row>
<b-col md="8" xl="6">
<alert variant="info" class="mb-4">
- <div class="font-weight-bold">
+ <div class="fw-bold">
{{ $t('pageKeyClear.alert.title') }}
</div>
<div>
@@ -26,19 +26,19 @@
<b-form-radio class="mb-1" value="NONE">
{{ $t('pageKeyClear.form.none') }}
</b-form-radio>
- <b-form-text id="key-clear-not-requested" class="ml-4 mb-3">
+ <b-form-text id="key-clear-not-requested" class="ms-4 mb-3">
{{ $t('pageKeyClear.form.keyClearNotRequested') }}
</b-form-text>
<b-form-radio class="mb-1" value="ALL">
{{ $t('pageKeyClear.form.clearAllLabel') }}
</b-form-radio>
- <b-form-text id="clear-all" class="ml-4 mb-3">
+ <b-form-text id="clear-all" class="ms-4 mb-3">
{{ $t('pageKeyClear.form.clearAllHeperText') }}
</b-form-text>
<b-form-radio class="mb-1" value="POWERVM_SYSKEY">
{{ $t('pageKeyClear.form.clearHypervisorSystemKeyLabel') }}
</b-form-radio>
- <b-form-text id="clear-hypervisor-key" class="ml-4 mb-3">
+ <b-form-text id="clear-hypervisor-key" class="ms-4 mb-3">
{{ $t('pageKeyClear.form.clearHypervisorSystemKeyHelperText') }}
</b-form-text>
<template v-if="username == 'service'">
@@ -71,11 +71,16 @@
import Alert from '@/components/Global/Alert';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'KeyClear',
components: { PageTitle, Alert },
mixins: [LoadingBarMixin, BVToastMixin],
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
@@ -88,22 +93,20 @@
},
methods: {
onKeyClearSubmit(valueSelected) {
- this.$bvModal
- .msgBoxConfirm(i18n.global.t('pageKeyClear.modal.clearAllMessage'), {
- title: i18n.global.t('pageKeyClear.modal.clearAllTitle'),
- okTitle: i18n.global.t('pageKeyClear.modal.clear'),
- okVariant: 'danger',
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'cancel',
- })
- .then((clearConfirmed) => {
- if (clearConfirmed) {
- this.$store
- .dispatch('keyClear/clearEncryptionKeys', valueSelected)
- .then((message) => this.successToast(message))
- .catch(({ message }) => this.errorToast(message));
- }
- });
+ this.$confirm(i18n.global.t('pageKeyClear.modal.clearAllMessage'), {
+ title: i18n.global.t('pageKeyClear.modal.clearAllTitle'),
+ okTitle: i18n.global.t('pageKeyClear.modal.clear'),
+ okVariant: 'danger',
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'cancel',
+ }).then((clearConfirmed) => {
+ if (clearConfirmed) {
+ this.$store
+ .dispatch('keyClear/clearEncryptionKeys', valueSelected)
+ .then((message) => this.successToast(message))
+ .catch(({ message }) => this.errorToast(message));
+ }
+ });
},
},
};
diff --git a/src/views/Operations/Kvm/KvmConsole.vue b/src/views/Operations/Kvm/KvmConsole.vue
index 64ee897..1ea6771 100644
--- a/src/views/Operations/Kvm/KvmConsole.vue
+++ b/src/views/Operations/Kvm/KvmConsole.vue
@@ -4,9 +4,7 @@
<b-row class="d-flex">
<b-col class="d-flex flex-column justify-content-end" cols="4">
<dl class="mb-2" sm="2" md="2">
- <dt class="d-inline font-weight-bold mr-1">
- {{ $t('pageKvm.status') }}:
- </dt>
+ <dt class="d-inline fw-bold me-1">{{ $t('pageKvm.status') }}:</dt>
<dd class="d-inline">
<status-icon :status="serverStatusIcon" />
<span class="d-none d-md-inline"> {{ serverStatus }}</span>
@@ -14,7 +12,7 @@
</dl>
</b-col>
- <b-col class="d-flex justify-content-end pr-1">
+ <b-col class="d-flex justify-content-end pe-1">
<b-button
v-if="isConnected"
variant="link"
@@ -125,7 +123,9 @@
this.resizeKvmWindow = throttle(() => {
setTimeout(that.setWidthToolbar, 0);
}, 1000);
- window.addEventListener('resize', this.resizeKvmWindow);
+ window.addEventListener('resize', this.resizeKvmWindow, {
+ passive: true,
+ });
this.rfb.addEventListener('connect', () => {
that.isConnected = true;
@@ -178,16 +178,16 @@
<style scoped lang="scss">
.button-ctrl-alt-delete {
- float: right;
+ float: inline-end;
}
.kvm-status {
- padding-top: $spacer / 2;
- padding-left: $spacer / 4;
+ padding-top: calc(#{$spacer} / 2);
+ padding-inline-start: calc(#{$spacer} / 4);
display: inline-block;
}
.margin-left-full-window {
- margin-left: 5px;
+ margin-inline-start: 5px;
}
</style>
diff --git a/src/views/Operations/RebootBmc/RebootBmc.vue b/src/views/Operations/RebootBmc/RebootBmc.vue
index d8c529c..e98b7da 100644
--- a/src/views/Operations/RebootBmc/RebootBmc.vue
+++ b/src/views/Operations/RebootBmc/RebootBmc.vue
@@ -40,6 +40,7 @@
import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'RebootBmc',
@@ -49,6 +50,10 @@
this.hideLoader();
next();
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
@@ -67,16 +72,14 @@
},
methods: {
onClick() {
- this.$bvModal
- .msgBoxConfirm(i18n.global.t('pageRebootBmc.modal.confirmMessage'), {
- title: i18n.global.t('pageRebootBmc.modal.confirmTitle'),
- okTitle: i18n.global.t('global.action.confirm'),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- })
- .then((confirmed) => {
- if (confirmed) this.rebootBmc();
- });
+ this.$confirm(i18n.global.t('pageRebootBmc.modal.confirmMessage'), {
+ title: i18n.global.t('pageRebootBmc.modal.confirmTitle'),
+ okTitle: i18n.global.t('global.action.confirm'),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ }).then((confirmed) => {
+ if (confirmed) this.rebootBmc();
+ });
},
rebootBmc() {
this.$store
diff --git a/src/views/Operations/SerialOverLan/SerialOverLanConsole.vue b/src/views/Operations/SerialOverLan/SerialOverLanConsole.vue
index 8f1c4bc..ca6a46d 100644
--- a/src/views/Operations/SerialOverLan/SerialOverLanConsole.vue
+++ b/src/views/Operations/SerialOverLan/SerialOverLanConsole.vue
@@ -17,7 +17,7 @@
<b-row class="d-flex">
<b-col class="d-flex flex-column justify-content-end">
<dl class="mb-2" sm="6" md="6">
- <dt class="d-inline font-weight-bold mr-1">
+ <dt class="d-inline fw-bold me-1">
{{ $t('pageSerialOverLan.status') }}:
</dt>
<dd class="d-inline">
diff --git a/src/views/Operations/ServerPowerOperations/BootSettings.vue b/src/views/Operations/ServerPowerOperations/BootSettings.vue
index ea66baa..b9ea035 100644
--- a/src/views/Operations/ServerPowerOperations/BootSettings.vue
+++ b/src/views/Operations/ServerPowerOperations/BootSettings.vue
@@ -13,7 +13,7 @@
v-model="form.bootOption"
:disabled="bootSourceOptions.length === 0"
:options="bootSourceOptions"
- @change="onChangeSelect"
+ @update:model-value="onChangeSelect"
>
</b-form-select>
</b-form-group>
@@ -118,7 +118,9 @@
this.$store
.dispatch('serverBootSettings/getTpmPolicy')
.finally(() =>
- this.$root.$emit('server-power-operations-boot-settings-complete'),
+ require('@/eventBus').default.$emit(
+ 'server-power-operations-boot-settings-complete',
+ ),
);
},
methods: {
diff --git a/src/views/Operations/ServerPowerOperations/ServerPowerOperations.vue b/src/views/Operations/ServerPowerOperations/ServerPowerOperations.vue
index 8dca96e..95a36d3 100644
--- a/src/views/Operations/ServerPowerOperations/ServerPowerOperations.vue
+++ b/src/views/Operations/ServerPowerOperations/ServerPowerOperations.vue
@@ -9,7 +9,9 @@
<b-row>
<b-col>
<dl>
- <dt>{{ $t('pageServerPowerOperations.serverStatus') }}</dt>
+ <dt>
+ {{ $t('pageServerPowerOperations.serverStatus') }}
+ </dt>
<dd
v-if="serverStatus === 'on'"
data-test-id="powerServerOps-text-hostStatus"
@@ -180,6 +182,7 @@
import i18n from '@/i18n';
import { privilegesId } from '@/store/modules/GlobalStore';
import { mapGetters } from 'vuex';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'ServerPowerOperations',
@@ -189,6 +192,10 @@
this.hideLoader();
next();
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
@@ -223,10 +230,9 @@
},
created() {
this.startLoader();
+ const eventBus = require('@/eventBus').default;
const bootSettingsPromise = new Promise((resolve) => {
- this.$root.$on('server-power-operations-boot-settings-complete', () =>
- resolve(),
- );
+ eventBus.$once('server-power-operations-boot-settings-complete', resolve);
});
Promise.all([
this.$store.dispatch('serverBootSettings/getBootSettings'),
@@ -252,17 +258,13 @@
};
if (this.form.rebootOption === 'orderly') {
- this.$bvModal
- .msgBoxConfirm(modalMessage, modalOptions)
- .then((confirmed) => {
- if (confirmed) this.$store.dispatch('controls/serverSoftReboot');
- });
+ this.confirmDialog(modalMessage, modalOptions).then((confirmed) => {
+ if (confirmed) this.$store.dispatch('controls/serverSoftReboot');
+ });
} else if (this.form.rebootOption === 'immediate') {
- this.$bvModal
- .msgBoxConfirm(modalMessage, modalOptions)
- .then((confirmed) => {
- if (confirmed) this.$store.dispatch('controls/serverHardReboot');
- });
+ this.confirmDialog(modalMessage, modalOptions).then((confirmed) => {
+ if (confirmed) this.$store.dispatch('controls/serverHardReboot');
+ });
}
},
shutdownServer() {
@@ -279,20 +281,19 @@
};
if (this.form.shutdownOption === 'orderly') {
- this.$bvModal
- .msgBoxConfirm(modalMessage, modalOptions)
- .then((confirmed) => {
- if (confirmed) this.$store.dispatch('controls/serverSoftPowerOff');
- });
+ this.confirmDialog(modalMessage, modalOptions).then((confirmed) => {
+ if (confirmed) this.$store.dispatch('controls/serverSoftPowerOff');
+ });
}
if (this.form.shutdownOption === 'immediate') {
- this.$bvModal
- .msgBoxConfirm(modalMessage, modalOptions)
- .then((confirmed) => {
- if (confirmed) this.$store.dispatch('controls/serverHardPowerOff');
- });
+ this.confirmDialog(modalMessage, modalOptions).then((confirmed) => {
+ if (confirmed) this.$store.dispatch('controls/serverHardPowerOff');
+ });
}
},
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
+ },
},
};
</script>
diff --git a/src/views/Operations/VirtualMedia/ModalConfigureConnection.vue b/src/views/Operations/VirtualMedia/ModalConfigureConnection.vue
index 682528b..b2eaaae 100644
--- a/src/views/Operations/VirtualMedia/ModalConfigureConnection.vue
+++ b/src/views/Operations/VirtualMedia/ModalConfigureConnection.vue
@@ -36,6 +36,7 @@
id="username"
v-model="form.username"
type="text"
+ autocomplete="username"
data-test-id="configureConnection-input-username"
/>
</b-form-group>
@@ -47,6 +48,7 @@
id="password"
v-model="form.password"
type="password"
+ autocomplete="current-password"
data-test-id="configureConnection-input-password"
/>
</b-form-group>
@@ -81,10 +83,6 @@
connection: {
type: Object,
default: null,
- validator: (prop) => {
- console.log(prop);
- return true;
- },
},
},
emits: ['ok'],
diff --git a/src/views/Operations/VirtualMedia/VirtualMedia.vue b/src/views/Operations/VirtualMedia/VirtualMedia.vue
index e158059..36fb909 100644
--- a/src/views/Operations/VirtualMedia/VirtualMedia.vue
+++ b/src/views/Operations/VirtualMedia/VirtualMedia.vue
@@ -69,7 +69,7 @@
<b-button
v-if="!device.isActive"
variant="primary"
- class="float-right"
+ class="float-end"
:disabled="!device.serverUri"
@click="startLegacy(device)"
>
@@ -78,7 +78,7 @@
<b-button
v-if="device.isActive"
variant="primary"
- class="float-right"
+ class="float-end"
@click="stopLegacy(device)"
>
{{ $t('pageVirtualMedia.stop') }}
@@ -90,6 +90,7 @@
</b-col>
</b-row>
<modal-configure-connection
+ v-model="showConfigureConnectionModal"
:connection="modalConfigureConnection"
@ok="saveConnection"
/>
@@ -106,15 +107,21 @@
import FormFile from '@/components/Global/FormFile';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'VirtualMedia',
components: { PageTitle, PageSection, ModalConfigureConnection, FormFile },
mixins: [BVToastMixin, LoadingBarMixin],
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
modalConfigureConnection: null,
+ showConfigureConnectionModal: false,
loadImageFromExternalServer:
process.env.VUE_APP_VIRTUAL_MEDIA_LIST_ENABLED === 'true'
? true
@@ -223,7 +230,7 @@
},
configureConnection(connectionData) {
this.modalConfigureConnection = connectionData;
- this.$bvModal.show('configure-connection');
+ this.showConfigureConnectionModal = true;
},
concatId(val) {
return val.split(' ').join('_').toLowerCase();
diff --git a/src/views/Overview/Overview.vue b/src/views/Overview/Overview.vue
index 2bf4205..1d64dff 100644
--- a/src/views/Overview/Overview.vue
+++ b/src/views/Overview/Overview.vue
@@ -6,21 +6,35 @@
:section-title="$t('pageOverview.systemInformation')"
class="mb-1"
>
- <b-card-group deck>
- <overview-server />
- <overview-firmware />
- </b-card-group>
- <b-card-group deck>
- <overview-network />
- <overview-power />
- </b-card-group>
+ <b-row class="row-cols-1 row-cols-md-2">
+ <b-col class="mb-3">
+ <overview-server class="h-100" />
+ </b-col>
+ <b-col class="mb-3">
+ <overview-firmware class="h-100" />
+ </b-col>
+ </b-row>
+ <b-row class="row-cols-1 row-cols-md-2">
+ <b-col class="mb-3">
+ <overview-network class="h-100" />
+ </b-col>
+ <b-col class="mb-3">
+ <overview-power class="h-100" />
+ </b-col>
+ </b-row>
</page-section>
<page-section :section-title="$t('pageOverview.statusInformation')">
- <b-card-group deck>
- <overview-events />
- <overview-inventory />
- <overview-dumps v-if="showDumps" />
- </b-card-group>
+ <b-row class="row-cols-1 row-cols-md-2">
+ <b-col class="mb-3">
+ <overview-events class="h-100" />
+ </b-col>
+ <b-col class="mb-3">
+ <overview-inventory class="h-100" />
+ </b-col>
+ <b-col v-if="showDumps" class="mb-3">
+ <overview-dumps class="h-100" />
+ </b-col>
+ </b-row>
</page-section>
</b-container>
</template>
@@ -58,33 +72,59 @@
return {
$t: useI18n().t,
showDumps: process.env.VUE_APP_ENV_NAME === 'ibm',
+ // Promise resolvers
+ dumpsResolver: null,
+ eventsResolver: null,
+ selResolver: null,
+ firmwareResolver: null,
+ inventoryResolver: null,
+ networkResolver: null,
+ powerResolver: null,
+ quicklinksResolver: null,
+ serverResolver: null,
};
},
created() {
this.startLoader();
+
const dumpsPromise = new Promise((resolve) => {
- this.$root.$on('overview-dumps-complete', () => resolve());
+ this.dumpsResolver = resolve;
+ this.$eventBus.on('overview-dumps-complete', () => resolve());
});
+
const eventsPromise = new Promise((resolve) => {
- this.$root.$on('overview-events-complete', () => resolve());
+ this.eventsResolver = resolve;
+ this.$eventBus.on('overview-events-complete', () => resolve());
});
+
const firmwarePromise = new Promise((resolve) => {
- this.$root.$on('overview-firmware-complete', () => resolve());
+ this.firmwareResolver = resolve;
+ this.$eventBus.on('overview-firmware-complete', () => resolve());
});
+
const inventoryPromise = new Promise((resolve) => {
- this.$root.$on('overview-inventory-complete', () => resolve());
+ this.inventoryResolver = resolve;
+ this.$eventBus.on('overview-inventory-complete', () => resolve());
});
+
const networkPromise = new Promise((resolve) => {
- this.$root.$on('overview-network-complete', () => resolve());
+ this.networkResolver = resolve;
+ this.$eventBus.on('overview-network-complete', () => resolve());
});
+
const powerPromise = new Promise((resolve) => {
- this.$root.$on('overview-power-complete', () => resolve());
+ this.powerResolver = resolve;
+ this.$eventBus.on('overview-power-complete', () => resolve());
});
+
const quicklinksPromise = new Promise((resolve) => {
- this.$root.$on('overview-quicklinks-complete', () => resolve());
+ this.quicklinksResolver = resolve;
+ this.$eventBus.on('overview-quicklinks-complete', () => resolve());
});
+
const serverPromise = new Promise((resolve) => {
- this.$root.$on('overview-server-complete', () => resolve());
+ this.serverResolver = resolve;
+ this.$eventBus.on('overview-server-complete', () => resolve());
});
const promises = [
@@ -97,7 +137,59 @@
serverPromise,
];
if (this.showDumps) promises.push(dumpsPromise);
- Promise.all(promises).finally(() => this.endLoader());
+ Promise.all(promises).finally(() => {
+ this.endLoader();
+ });
+ },
+ beforeUnmount() {
+ // Clean up event listeners
+ this.$eventBus.off('overview-dumps-complete', this.handleDumpsComplete);
+ this.$eventBus.off('overview-events-complete', this.handleEventsComplete);
+ this.$eventBus.off('overview-sel-complete', this.handleSelComplete);
+ this.$eventBus.off(
+ 'overview-firmware-complete',
+ this.handleFirmwareComplete,
+ );
+ this.$eventBus.off(
+ 'overview-inventory-complete',
+ this.handleInventoryComplete,
+ );
+ this.$eventBus.off('overview-network-complete', this.handleNetworkComplete);
+ this.$eventBus.off('overview-power-complete', this.handlePowerComplete);
+ this.$eventBus.off(
+ 'overview-quicklinks-complete',
+ this.handleQuicklinksComplete,
+ );
+ this.$eventBus.off('overview-server-complete', this.handleServerComplete);
+ },
+ methods: {
+ handleDumpsComplete() {
+ if (this.dumpsResolver) this.dumpsResolver();
+ },
+ handleEventsComplete() {
+ if (this.eventsResolver) this.eventsResolver();
+ },
+ handleSelComplete() {
+ if (this.selResolver) this.selResolver();
+ },
+ handleFirmwareComplete() {
+ if (this.firmwareResolver) this.firmwareResolver();
+ },
+ handleInventoryComplete() {
+ if (this.inventoryResolver) this.inventoryResolver();
+ },
+ handleNetworkComplete() {
+ if (this.networkResolver) this.networkResolver();
+ },
+ handlePowerComplete() {
+ if (this.powerResolver) this.powerResolver();
+ },
+ handleQuicklinksComplete() {
+ if (this.quicklinksResolver) this.quicklinksResolver();
+ },
+ handleServerComplete() {
+ if (this.serverResolver) this.serverResolver();
+ },
},
};
</script>
diff --git a/src/views/Overview/OverviewCard.vue b/src/views/Overview/OverviewCard.vue
index 7cfe558..d6955b6 100644
--- a/src/views/Overview/OverviewCard.vue
+++ b/src/views/Overview/OverviewCard.vue
@@ -14,7 +14,7 @@
<span v-if="downloadButton">{{ $t('global.action.download') }}</span>
<span v-if="exportButton">{{ $t('global.action.exportAll') }}</span>
</b-button>
- <span v-if="exportButton || downloadButton" class="pl-2 pr-2">|</span>
+ <span v-if="exportButton || downloadButton" class="ps-2 pe-2">|</span>
<b-link :to="to">{{ $t('pageOverview.viewMore') }}</b-link>
</div>
</div>
diff --git a/src/views/Overview/OverviewDumps.vue b/src/views/Overview/OverviewDumps.vue
index ba7d7e2..abdaf47 100644
--- a/src/views/Overview/OverviewDumps.vue
+++ b/src/views/Overview/OverviewDumps.vue
@@ -41,7 +41,7 @@
},
created() {
this.$store.dispatch('dumps/getAllDumps').finally(() => {
- this.$root.$emit('overview-dumps-complete');
+ this.$eventBus.$emit('overview-dumps-complete');
});
},
methods: {
diff --git a/src/views/Overview/OverviewEvents.vue b/src/views/Overview/OverviewEvents.vue
index 147f30c..65ac0ea 100644
--- a/src/views/Overview/OverviewEvents.vue
+++ b/src/views/Overview/OverviewEvents.vue
@@ -72,7 +72,7 @@
},
created() {
this.$store.dispatch('eventLog/getEventLogData').finally(() => {
- this.$root.$emit('overview-events-complete');
+ this.$eventBus.$emit('overview-events-complete');
});
},
methods: {
diff --git a/src/views/Overview/OverviewFirmware.vue b/src/views/Overview/OverviewFirmware.vue
index 0be920f..0b58a6c 100644
--- a/src/views/Overview/OverviewFirmware.vue
+++ b/src/views/Overview/OverviewFirmware.vue
@@ -61,7 +61,7 @@
},
created() {
this.$store.dispatch('firmware/getFirmwareInformation').finally(() => {
- this.$root.$emit('overview-firmware-complete');
+ this.$eventBus.$emit('overview-firmware-complete');
});
},
};
diff --git a/src/views/Overview/OverviewInventory.vue b/src/views/Overview/OverviewInventory.vue
index 4d923e1..0c92b8e 100644
--- a/src/views/Overview/OverviewInventory.vue
+++ b/src/views/Overview/OverviewInventory.vue
@@ -51,7 +51,7 @@
},
created() {
this.$store.dispatch('system/getSystem').finally(() => {
- this.$root.$emit('overview-inventory-complete');
+ this.$eventBus.$emit('overview-inventory-complete');
});
},
methods: {
diff --git a/src/views/Overview/OverviewNetwork.vue b/src/views/Overview/OverviewNetwork.vue
index c2a7693..63f5533 100644
--- a/src/views/Overview/OverviewNetwork.vue
+++ b/src/views/Overview/OverviewNetwork.vue
@@ -70,7 +70,7 @@
},
created() {
this.$store.dispatch('network/getEthernetData').finally(() => {
- this.$root.$emit('overview-network-complete');
+ this.$eventBus.$emit('overview-network-complete');
});
},
};
diff --git a/src/views/Overview/OverviewPower.vue b/src/views/Overview/OverviewPower.vue
index 74a737e..1d3d4a0 100644
--- a/src/views/Overview/OverviewPower.vue
+++ b/src/views/Overview/OverviewPower.vue
@@ -47,7 +47,7 @@
},
created() {
this.$store.dispatch('powerControl/getPowerControl').finally(() => {
- this.$root.$emit('overview-power-complete');
+ this.$eventBus.$emit('overview-power-complete');
});
},
};
diff --git a/src/views/Overview/OverviewQuickLinks.vue b/src/views/Overview/OverviewQuickLinks.vue
index abcf9fa..f50e7b2 100644
--- a/src/views/Overview/OverviewQuickLinks.vue
+++ b/src/views/Overview/OverviewQuickLinks.vue
@@ -49,7 +49,7 @@
},
created() {
Promise.all([this.$store.dispatch('global/getBmcTime')]).finally(() => {
- this.$root.$emit('overview-quicklinks-complete');
+ this.$eventBus.$emit('overview-quicklinks-complete');
});
},
};
diff --git a/src/views/Overview/OverviewServer.vue b/src/views/Overview/OverviewServer.vue
index fe0e41d..b9eda24 100644
--- a/src/views/Overview/OverviewServer.vue
+++ b/src/views/Overview/OverviewServer.vue
@@ -55,7 +55,7 @@
},
created() {
this.$store.dispatch('system/getSystem').finally(() => {
- this.$root.$emit('overview-server-complete');
+ this.$eventBus.$emit('overview-server-complete');
});
},
};
diff --git a/src/views/ProfileSettings/ProfileSettings.vue b/src/views/ProfileSettings/ProfileSettings.vue
index 73ee63f..2e6cec6 100644
--- a/src/views/ProfileSettings/ProfileSettings.vue
+++ b/src/views/ProfileSettings/ProfileSettings.vue
@@ -18,6 +18,16 @@
</b-row>
<b-form @submit.prevent="submitForm">
+ <!-- Hidden username field for browser autocomplete accessibility -->
+ <input
+ type="text"
+ name="username"
+ :value="username"
+ autocomplete="username"
+ class="visually-hidden"
+ aria-hidden="true"
+ tabindex="-1"
+ />
<b-row>
<b-col sm="8" md="6" xl="3">
<page-section
@@ -33,6 +43,7 @@
id="old-password"
v-model="form.currentPassword"
type="password"
+ autocomplete="current-password"
data-test-id="profileSettings-input-ocurrentPassword"
class="form-control-with-button"
/>
@@ -57,6 +68,7 @@
v-model="form.newPassword"
type="password"
aria-describedby="password-help-block"
+ autocomplete="new-password"
:state="getValidationState(v$.form.newPassword)"
data-test-id="profileSettings-input-newPassword"
class="form-control-with-button"
@@ -89,6 +101,7 @@
id="password-confirmation"
v-model="form.confirmPassword"
type="password"
+ autocomplete="new-password"
:state="getValidationState(v$.form.confirmPassword)"
data-test-id="profileSettings-input-confirmPassword"
class="form-control-with-button"
diff --git a/src/views/ResourceManagement/Power.vue b/src/views/ResourceManagement/Power.vue
index ac70091..5248efc 100644
--- a/src/views/ResourceManagement/Power.vue
+++ b/src/views/ResourceManagement/Power.vue
@@ -19,7 +19,7 @@
<b-form @submit.prevent="submitForm">
<b-form-group :disabled="loading">
- <b-row>
+ <b-row class="mb-3">
<b-col sm="8" md="6" xl="12">
<b-form-group :label="$t('pagePower.powerCapSettingLabel')">
<b-form-checkbox
@@ -33,7 +33,7 @@
</b-col>
</b-row>
- <b-row>
+ <b-row class="mb-3">
<b-col sm="8" md="6" xl="3">
<b-form-group
id="input-group-1"
@@ -75,6 +75,7 @@
variant="primary"
type="submit"
data-test-id="power-button-savePowerCapValue"
+ class="mt-3"
>
{{ $t('global.action.save') }}
</b-button>
diff --git a/src/views/SecurityAndAccess/Certificates/Certificates.vue b/src/views/SecurityAndAccess/Certificates/Certificates.vue
index a55b66c..29c1436 100644
--- a/src/views/SecurityAndAccess/Certificates/Certificates.vue
+++ b/src/views/SecurityAndAccess/Certificates/Certificates.vue
@@ -32,11 +32,11 @@
</b-col>
</b-row>
<b-row>
- <b-col xl="11" class="text-right">
+ <b-col xl="11" class="text-end">
<b-button
- v-b-modal.generate-csr
data-test-id="certificates-button-generateCsr"
variant="link"
+ @click="showCsr = true"
>
<icon-add />
{{ $t('pageCertificates.generateCsr') }}
@@ -57,6 +57,7 @@
responsive="md"
show-empty
hover
+ thead-class="table-light"
:busy="isBusy"
:fields="fields"
:items="tableItems"
@@ -94,8 +95,12 @@
</b-row>
<!-- Modals -->
- <modal-upload-certificate :certificate="modalCertificate" @ok="onModalOk" />
- <modal-generate-csr />
+ <modal-upload-certificate
+ v-model="showUpload"
+ :certificate="modalCertificate"
+ @ok="onModalOk"
+ />
+ <modal-generate-csr v-model="showCsr" />
</b-container>
</template>
@@ -115,6 +120,7 @@
import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'Certificates',
@@ -134,11 +140,17 @@
this.hideLoader();
next();
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
isBusy: true,
modalCertificate: null,
+ showUpload: false,
+ showCsr: false,
fileTypeCorrect: undefined,
fields: [
{
@@ -164,7 +176,7 @@
{
key: 'actions',
label: '',
- tdClass: 'text-right text-nowrap',
+ tdClass: 'text-end text-nowrap',
},
],
};
@@ -240,25 +252,23 @@
},
initModalUploadCertificate(certificate = null) {
this.modalCertificate = certificate;
- this.$bvModal.show('upload-certificate');
+ this.showUpload = true;
},
initModalDeleteCertificate(certificate) {
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t('pageCertificates.modal.deleteConfirmMessage', {
- issuedBy: certificate.issuedBy,
- certificate: certificate.certificate,
- }),
- {
- title: i18n.global.t('pageCertificates.deleteCertificate'),
- okTitle: i18n.global.t('global.action.delete'),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfirmed) => {
- if (deleteConfirmed) this.deleteCertificate(certificate);
- });
+ this.confirmDialog(
+ i18n.global.t('pageCertificates.modal.deleteConfirmMessage', {
+ issuedBy: certificate.issuedBy,
+ certificate: certificate.certificate,
+ }),
+ {
+ title: i18n.global.t('pageCertificates.deleteCertificate'),
+ okTitle: i18n.global.t('global.action.delete'),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ ).then((deleteConfirmed) => {
+ if (deleteConfirmed) this.deleteCertificate(certificate);
+ });
},
onModalOk({ addNew, file, type, location }) {
if (addNew) {
@@ -342,6 +352,9 @@
const fileTypeExtension = file.name.split('.').pop();
return fileTypeExtension === 'pem';
},
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
+ },
},
};
</script>
diff --git a/src/views/SecurityAndAccess/Certificates/ModalGenerateCsr.vue b/src/views/SecurityAndAccess/Certificates/ModalGenerateCsr.vue
index a74a1e4..53364de 100644
--- a/src/views/SecurityAndAccess/Certificates/ModalGenerateCsr.vue
+++ b/src/views/SecurityAndAccess/Certificates/ModalGenerateCsr.vue
@@ -3,6 +3,7 @@
<b-modal
id="generate-csr"
ref="modal"
+ v-model="isVisible"
size="lg"
no-stacking
:title="$t('pageCertificates.modal.generateACertificateSigningRequest')"
@@ -157,7 +158,8 @@
<b-col lg="6">
<b-form-group label-for="contact-person">
<template #label>
- {{ $t('pageCertificates.modal.contactPerson') }} -
+ {{ $t('pageCertificates.modal.contactPerson') }}
+ -
<span class="form-text d-inline">
{{ $t('global.form.optional') }}
</span>
@@ -175,7 +177,8 @@
<b-col lg="6">
<b-form-group label-for="email-address">
<template #label>
- {{ $t('pageCertificates.modal.emailAddress') }} -
+ {{ $t('pageCertificates.modal.emailAddress') }}
+ -
<span class="form-text d-inline">
{{ $t('global.form.optional') }}
</span>
@@ -193,7 +196,8 @@
<b-col lg="12">
<b-form-group label-for="alternate-name">
<template #label>
- {{ $t('pageCertificates.modal.alternateName') }} -
+ {{ $t('pageCertificates.modal.alternateName') }}
+ -
<span class="form-text d-inline">
{{ $t('global.form.optional') }}
</span>
@@ -218,7 +222,8 @@
data-test-id="modalGenerateCsr-input-alternateName"
>
<template #add-button-text>
- <icon-add /> {{ $t('global.action.add') }}
+ <icon-add />
+ {{ $t('global.action.add') }}
</template>
</b-form-tags>
</b-form-group>
@@ -311,7 +316,7 @@
</b-row>
</b-container>
</b-form>
- <template #modal-footer="{ ok, cancel }">
+ <template #footer="{ ok, cancel }">
<b-button variant="secondary" @click="cancel()">
{{ $t('global.action.cancel') }}
</b-button>
@@ -328,13 +333,14 @@
</b-modal>
<b-modal
id="csr-string"
+ v-model="showCsrString"
no-stacking
size="lg"
:title="$t('pageCertificates.modal.certificateSigningRequest')"
@hidden="onHiddenCsrStringModal"
>
{{ csrString }}
- <template #modal-footer>
+ <template #footer>
<b-btn variant="secondary" @click="copyCsrString">
<template v-if="csrStringCopied">
<icon-checkmark />
@@ -375,6 +381,13 @@
name: 'ModalGenerateCsr',
components: { IconAdd, IconCheckmark },
mixins: [BVToastMixin, VuelidateMixin],
+ props: {
+ modelValue: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ emits: ['update:modelValue'],
setup() {
return {
v$: useVuelidate(),
@@ -383,6 +396,7 @@
data() {
return {
$t: useI18n().t,
+ showCsrString: false,
form: {
certificateType: null,
country: null,
@@ -410,6 +424,14 @@
};
},
computed: {
+ isVisible: {
+ get() {
+ return this.modelValue;
+ },
+ set(value) {
+ this.$emit('update:modelValue', value);
+ },
+ },
certificateTypes() {
return this.$store.getters['certificates/certificateTypes'];
},
@@ -457,7 +479,7 @@
.dispatch('certificates/generateCsr', this.form)
.then(({ data: { CSRString } }) => {
this.csrString = CSRString;
- this.$bvModal.show('csr-string');
+ this.showCsrString = true;
this.v$.$reset();
});
},
diff --git a/src/views/SecurityAndAccess/Certificates/ModalUploadCertificate.vue b/src/views/SecurityAndAccess/Certificates/ModalUploadCertificate.vue
index 7a40bfb..927163e 100644
--- a/src/views/SecurityAndAccess/Certificates/ModalUploadCertificate.vue
+++ b/src/views/SecurityAndAccess/Certificates/ModalUploadCertificate.vue
@@ -1,18 +1,16 @@
<template>
- <b-modal id="upload-certificate" ref="modal" @ok="onOk" @hidden="resetForm">
- <template #modal-title>
- <template v-if="certificate">
- {{ $t('pageCertificates.replaceCertificate') }}
- </template>
- <template v-else>
- {{ $t('pageCertificates.addNewCertificate') }}
- </template>
- </template>
+ <b-modal
+ id="upload-certificate"
+ ref="modal"
+ :title="modalTitle"
+ @ok="onOk"
+ @hidden="resetForm"
+ >
<b-form>
<!-- Replace Certificate type -->
<template v-if="certificate !== null">
<dl class="mb-4">
- <dt>{{ $t('pageCertificates.modal.certificateType') }}</dt>
+ <dt>{{ i18n.t('pageCertificates.modal.certificateType') }}</dt>
<dd>{{ certificate.certificate }}</dd>
</dl>
</template>
@@ -20,7 +18,7 @@
<!-- Add new Certificate type -->
<template v-else>
<b-form-group
- :label="$t('pageCertificates.modal.certificateType')"
+ :label="i18n.t('pageCertificates.modal.certificateType')"
label-for="certificate-type"
>
<b-form-select
@@ -33,13 +31,13 @@
</b-form-select>
<b-form-invalid-feedback role="alert">
<template v-if="v$.form.certificateType.required.$invalid">
- {{ $t('global.form.fieldRequired') }}
+ {{ i18n.t('global.form.fieldRequired') }}
</template>
</b-form-invalid-feedback>
</b-form-group>
</template>
- <b-form-group :label="$t('pageCertificates.modal.certificateFile')">
+ <b-form-group :label="i18n.t('pageCertificates.modal.certificateFile')">
<form-file
id="certificate-file"
v-model="form.file"
@@ -48,7 +46,7 @@
>
<template #invalid>
<b-form-invalid-feedback role="alert">
- {{ $t('global.form.required') }}
+ {{ i18n.t('global.form.required') }}
</b-form-invalid-feedback>
</template>
</form-file>
@@ -56,14 +54,14 @@
</b-form>
<template #modal-ok>
<template v-if="certificate">
- {{ $t('global.action.replace') }}
+ {{ i18n.t('global.action.replace') }}
</template>
<template v-else>
- {{ $t('global.action.add') }}
+ {{ i18n.t('global.action.add') }}
</template>
</template>
<template #modal-cancel>
- {{ $t('global.action.cancel') }}
+ {{ i18n.t('global.action.cancel') }}
</template>
</b-modal>
</template>
@@ -94,13 +92,14 @@
},
emits: ['ok'],
setup() {
+ const i18n = useI18n();
return {
v$: useVuelidate(),
+ i18n,
};
},
data() {
return {
- $t: useI18n().t,
form: {
certificateType: null,
file: null,
@@ -108,6 +107,11 @@
};
},
computed: {
+ modalTitle() {
+ return this.certificate
+ ? this.i18n.t('pageCertificates.replaceCertificate')
+ : this.i18n.t('pageCertificates.addNewCertificate');
+ },
certificateTypes() {
return this.$store.getters['certificates/availableUploadTypes'];
},
diff --git a/src/views/SecurityAndAccess/Ldap/Ldap.vue b/src/views/SecurityAndAccess/Ldap/Ldap.vue
index 6de491a..a39b800 100644
--- a/src/views/SecurityAndAccess/Ldap/Ldap.vue
+++ b/src/views/SecurityAndAccess/Ldap/Ldap.vue
@@ -24,7 +24,7 @@
<b-form-group
class="m-0"
:label="$t('pageLdap.ariaLabel.ldapSettings')"
- label-class="sr-only"
+ label-class="visually-hidden-focusable"
:disabled="!form.ldapAuthenticationEnabled || loading"
>
<b-row>
@@ -50,12 +50,16 @@
</b-form-checkbox>
</b-form-group>
<dl>
- <dt>{{ $t('pageLdap.form.caCertificateValidUntil') }}</dt>
+ <dt>
+ {{ $t('pageLdap.form.caCertificateValidUntil') }}
+ </dt>
<dd v-if="caCertificateExpiration">
{{ $filters.formatDate(caCertificateExpiration) }}
</dd>
<dd v-else>--</dd>
- <dt>{{ $t('pageLdap.form.ldapCertificateValidUntil') }}</dt>
+ <dt>
+ {{ $t('pageLdap.form.ldapCertificateValidUntil') }}
+ </dt>
<dd v-if="ldapCertificateExpiration">
{{ $filters.formatDate(ldapCertificateExpiration) }}
</dd>
@@ -122,6 +126,7 @@
<b-form-input
id="bind-dn"
v-model="form.bindDn"
+ autocomplete="username"
data-test-id="ldap-input-bindDn"
:state="getValidationState(v$.form.bindDn)"
@change="v$.form.bindDn.$touch()"
@@ -143,6 +148,7 @@
id="bind-password"
v-model="form.bindPassword"
type="password"
+ autocomplete="current-password"
:state="getValidationState(v$.form.bindPassword)"
class="form-control-with-button"
@change="v$.form.bindPassword.$touch()"
@@ -173,7 +179,8 @@
<b-col sm="6" xl="4">
<b-form-group label-for="user-id-attribute">
<template #label>
- {{ $t('pageLdap.form.userIdAttribute') }} -
+ {{ $t('pageLdap.form.userIdAttribute') }}
+ -
<span class="form-text d-inline">
{{ $t('global.form.optional') }}
</span>
@@ -189,7 +196,8 @@
<b-col sm="6" xl="4">
<b-form-group label-for="group-id-attribute">
<template #label>
- {{ $t('pageLdap.form.groupIdAttribute') }} -
+ {{ $t('pageLdap.form.groupIdAttribute') }}
+ -
<span class="form-text d-inline">
{{ $t('global.form.optional') }}
</span>
diff --git a/src/views/SecurityAndAccess/Ldap/ModalAddRoleGroup.vue b/src/views/SecurityAndAccess/Ldap/ModalAddRoleGroup.vue
index f4dcb82..38ff473 100644
--- a/src/views/SecurityAndAccess/Ldap/ModalAddRoleGroup.vue
+++ b/src/views/SecurityAndAccess/Ldap/ModalAddRoleGroup.vue
@@ -1,13 +1,11 @@
<template>
- <b-modal id="modal-role-group" ref="modal" @ok="onOk" @hidden="resetForm">
- <template #modal-title>
- <template v-if="roleGroup">
- {{ $t('pageLdap.modal.editRoleGroup') }}
- </template>
- <template v-else>
- {{ $t('pageLdap.modal.addNewRoleGroup') }}
- </template>
- </template>
+ <b-modal
+ id="modal-role-group"
+ ref="modal"
+ :title="modalTitle"
+ @ok="onOk"
+ @hidden="resetForm"
+ >
<b-container>
<b-row>
<b-col sm="8">
@@ -15,7 +13,7 @@
<!-- Edit role group -->
<template v-if="roleGroup !== null">
<dl class="mb-4">
- <dt>{{ $t('pageLdap.modal.groupName') }}</dt>
+ <dt>{{ i18n.t('pageLdap.modal.groupName') }}</dt>
<dd style="word-break: break-all">{{ form.groupName }}</dd>
</dl>
</template>
@@ -23,7 +21,7 @@
<!-- Add new role group -->
<template v-else>
<b-form-group
- :label="$t('pageLdap.modal.groupName')"
+ :label="i18n.t('pageLdap.modal.groupName')"
label-for="role-group-name"
>
<b-form-input
@@ -33,13 +31,13 @@
@input="v$.form.groupName.$touch()"
/>
<b-form-invalid-feedback role="alert">
- {{ $t('global.form.fieldRequired') }}
+ {{ i18n.t('global.form.fieldRequired') }}
</b-form-invalid-feedback>
</b-form-group>
</template>
<b-form-group
- :label="$t('pageLdap.modal.groupPrivilege')"
+ :label="i18n.t('pageLdap.modal.groupPrivilege')"
label-for="privilege"
>
<b-form-select
@@ -51,28 +49,28 @@
>
<template v-if="!roleGroup" #first>
<b-form-select-option :value="null" disabled>
- {{ $t('global.form.selectAnOption') }}
+ {{ i18n.t('global.form.selectAnOption') }}
</b-form-select-option>
</template>
</b-form-select>
<b-form-invalid-feedback role="alert">
- {{ $t('global.form.fieldRequired') }}
+ {{ i18n.t('global.form.fieldRequired') }}
</b-form-invalid-feedback>
</b-form-group>
</b-form>
</b-col>
</b-row>
</b-container>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button variant="secondary" @click="cancel()">
- {{ $t('global.action.cancel') }}
+ {{ i18n.t('global.action.cancel') }}
</b-button>
<b-button form="role-group" type="submit" variant="primary" @click="onOk">
<template v-if="roleGroup">
- {{ $t('global.action.save') }}
+ {{ i18n.t('global.action.save') }}
</template>
<template v-else>
- {{ $t('global.action.add') }}
+ {{ i18n.t('global.action.add') }}
</template>
</b-button>
</template>
@@ -102,13 +100,14 @@
},
emits: ['ok', 'hidden'],
setup() {
+ const i18n = useI18n();
return {
v$: useVuelidate(),
+ i18n,
};
},
data() {
return {
- $t: useI18n().t,
form: {
groupName: null,
groupPrivilege: null,
@@ -116,6 +115,11 @@
};
},
computed: {
+ modalTitle() {
+ return this.roleGroup
+ ? this.i18n.t('pageLdap.modal.editRoleGroup')
+ : this.i18n.t('pageLdap.modal.addNewRoleGroup');
+ },
accountRoles() {
return this.$store.getters['userManagement/accountRoles'];
},
diff --git a/src/views/SecurityAndAccess/Ldap/TableRoleGroups.vue b/src/views/SecurityAndAccess/Ldap/TableRoleGroups.vue
index f73d927..8b8b3ac 100644
--- a/src/views/SecurityAndAccess/Ldap/TableRoleGroups.vue
+++ b/src/views/SecurityAndAccess/Ldap/TableRoleGroups.vue
@@ -8,7 +8,7 @@
</b-col>
</b-row>
<b-row>
- <b-col class="text-right" md="9">
+ <b-col class="text-end" md="9">
<b-btn
variant="primary"
:disabled="!isServiceEnabled"
@@ -23,7 +23,9 @@
<b-col md="9">
<table-toolbar
ref="toolbar"
- :selected-items-count="selectedRows.length"
+ :selected-items-count="
+ Array.isArray(selectedRows) ? selectedRows.length : 0
+ "
:actions="batchActions"
@clear-selected="clearSelectedRows($refs.table)"
@batch-action="onBatchAction"
@@ -35,8 +37,9 @@
show-empty
no-select-on-click
hover
- no-sort-reset
+ must-sort
sort-icon-left
+ thead-class="table-light"
:busy="isBusy"
:items="tableItems"
:fields="fields"
@@ -49,9 +52,11 @@
v-model="tableHeaderCheckboxModel"
:indeterminate="tableHeaderCheckboxIndeterminate"
:disabled="!isServiceEnabled"
- @change="onChangeHeaderCheckbox($refs.table)"
+ @change="onChangeHeaderCheckbox($refs.table, $event)"
>
- <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectAll') }}
+ </span>
</b-form-checkbox>
</template>
<template #cell(checkbox)="row">
@@ -60,7 +65,9 @@
:disabled="!isServiceEnabled"
@change="toggleSelectRow($refs.table, row.index)"
>
- <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectItem') }}
+ </span>
</b-form-checkbox>
</template>
@@ -84,6 +91,7 @@
</b-col>
</b-row>
<modal-add-role-group
+ v-model="showRoleGroupModal"
:role-group="activeRoleGroup"
@ok="saveRoleGroup"
@hidden="activeRoleGroup = null"
@@ -110,6 +118,7 @@
import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
components: {
@@ -122,11 +131,16 @@
TableToolbar,
},
mixins: [BVTableSelectableMixin, BVToastMixin, LoadingBarMixin],
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
isBusy: true,
activeRoleGroup: null,
+ showRoleGroupModal: false,
fields: [
{
key: 'checkbox',
@@ -146,7 +160,7 @@
key: 'actions',
sortable: false,
label: '',
- tdClass: 'text-right',
+ tdClass: 'text-end',
},
],
batchActions: [
@@ -190,31 +204,30 @@
},
methods: {
onBatchAction() {
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t(
- 'pageLdap.modal.deleteRoleGroupBatchConfirmMessage',
- this.selectedRows.length,
- ),
- {
- title: i18n.global.t('pageLdap.modal.deleteRoleGroup'),
- okTitle: i18n.global.t('global.action.delete'),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfirmed) => {
- if (deleteConfirmed) {
- this.startLoader();
- this.$store
- .dispatch('ldap/deleteRoleGroup', {
- roleGroups: this.selectedRows,
- })
- .then((success) => this.successToast(success))
- .catch(({ message }) => this.errorToast(message))
- .finally(() => this.endLoader());
- }
- });
+ const count = this.selectedRows.length;
+ this.confirmDialog(
+ i18n.global.t(
+ 'pageLdap.modal.deleteRoleGroupBatchConfirmMessage',
+ count,
+ ),
+ {
+ title: i18n.global.t('pageLdap.modal.deleteRoleGroup'),
+ okTitle: i18n.global.t('global.action.delete'),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ ).then((deleteConfirmed) => {
+ if (deleteConfirmed) {
+ this.startLoader();
+ this.$store
+ .dispatch('ldap/deleteRoleGroup', {
+ roleGroups: this.selectedRows,
+ })
+ .then((success) => this.successToast(success))
+ .catch(({ message }) => this.errorToast(message))
+ .finally(() => this.endLoader());
+ }
+ });
},
onTableRowAction(action, row) {
switch (action) {
@@ -222,34 +235,37 @@
this.initRoleGroupModal(row);
break;
case 'delete':
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t('pageLdap.modal.deleteRoleGroupConfirmMessage', {
- groupName: row.groupName,
- }),
- {
- title: i18n.global.t('pageLdap.modal.deleteRoleGroup'),
- okTitle: i18n.global.t('global.action.delete'),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfirmed) => {
- if (deleteConfirmed) {
- this.startLoader();
- this.$store
- .dispatch('ldap/deleteRoleGroup', { roleGroups: [row] })
- .then((success) => this.successToast(success))
- .catch(({ message }) => this.errorToast(message))
- .finally(() => this.endLoader());
- }
- });
+ this.confirmDialog(
+ i18n.global.t('pageLdap.modal.deleteRoleGroupConfirmMessage', {
+ groupName: row.groupName,
+ }),
+ {
+ title: i18n.global.t('pageLdap.modal.deleteRoleGroup'),
+ okTitle: i18n.global.t('global.action.delete'),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ ).then((deleteConfirmed) => {
+ if (deleteConfirmed) {
+ this.startLoader();
+ this.$store
+ .dispatch('ldap/deleteRoleGroup', {
+ roleGroups: [row],
+ })
+ .then((success) => this.successToast(success))
+ .catch(({ message }) => this.errorToast(message))
+ .finally(() => this.endLoader());
+ }
+ });
break;
}
},
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
+ },
initRoleGroupModal(roleGroup) {
this.activeRoleGroup = roleGroup;
- this.$bvModal.show('modal-role-group');
+ this.showRoleGroupModal = true;
},
saveRoleGroup({ addNew, groupName, groupPrivilege }) {
this.activeRoleGroup = null;
diff --git a/src/views/SecurityAndAccess/Policies/Policies.vue b/src/views/SecurityAndAccess/Policies/Policies.vue
index 58d8363..b9a4323 100644
--- a/src/views/SecurityAndAccess/Policies/Policies.vue
+++ b/src/views/SecurityAndAccess/Policies/Policies.vue
@@ -5,7 +5,7 @@
<b-col md="8">
<b-row v-if="!modifySSHPolicyDisabled" class="setting-section">
<b-col class="d-flex align-items-center justify-content-between">
- <dl class="mr-3 w-75">
+ <dl class="me-3 w-75">
<dt>{{ $t('pagePolicies.ssh') }}</dt>
<dd>
{{ $t('pagePolicies.sshDescription') }}
@@ -18,7 +18,7 @@
switch
@change="changeSshProtocolState"
>
- <span class="sr-only">
+ <span class="visually-hidden-focusable">
{{ $t('pagePolicies.ssh') }}
</span>
<span v-if="sshProtocolState">
@@ -30,7 +30,7 @@
</b-row>
<b-row class="setting-section">
<b-col class="d-flex align-items-center justify-content-between">
- <dl class="mt-3 mr-3 w-75">
+ <dl class="mt-3 me-3 w-75">
<dt>{{ $t('pagePolicies.ipmi') }}</dt>
<dd>
{{ $t('pagePolicies.ipmiDescription') }}
@@ -43,7 +43,7 @@
switch
@change="changeIpmiProtocolState"
>
- <span class="sr-only">
+ <span class="visually-hidden-focusable">
{{ $t('pagePolicies.ipmi') }}
</span>
<span v-if="ipmiProtocolState">
@@ -55,7 +55,7 @@
</b-row>
<b-row class="setting-section">
<b-col class="d-flex align-items-center justify-content-between">
- <dl class="mt-3 mr-3 w-75">
+ <dl class="mt-3 me-3 w-75">
<dt>{{ $t('pagePolicies.vtpm') }}</dt>
<dd>
{{ $t('pagePolicies.vtpmDescription') }}
@@ -68,7 +68,7 @@
switch
@change="changeVtpmState"
>
- <span class="sr-only">
+ <span class="visually-hidden-focusable">
{{ $t('pagePolicies.vtpm') }}
</span>
<span v-if="vtpmState">
@@ -80,7 +80,7 @@
</b-row>
<b-row class="setting-section">
<b-col class="d-flex align-items-center justify-content-between">
- <dl class="mt-3 mr-3 w-75">
+ <dl class="mt-3 me-3 w-75">
<dt>{{ $t('pagePolicies.rtad') }}</dt>
<dd>
{{ $t('pagePolicies.rtadDescription') }}
@@ -93,7 +93,7 @@
switch
@change="changeRtadState"
>
- <span class="sr-only">
+ <span class="visually-hidden-focusable">
{{ $t('pagePolicies.rtad') }}
</span>
<span v-if="rtadState">
@@ -105,7 +105,7 @@
</b-row>
<b-row class="setting-section">
<b-col class="d-flex align-items-center justify-content-between">
- <dl class="mt-3 mr-3 w-75">
+ <dl class="mt-3 me-3 w-75">
<dt>{{ $t('pagePolicies.webSessionTimeOut') }}</dt>
<dd>
{{ $t('pagePolicies.webSessionTimeOutDescription') }}
@@ -117,7 +117,7 @@
id="session-timeout-options"
v-model="sessionTimeoutState"
:options="sessionTimeOutOptions"
- @change="saveSessionTimeoutValue"
+ @update:model-value="saveSessionTimeoutValue"
>
<template #first>
<b-form-select-option :value="null" disabled>
@@ -154,12 +154,30 @@
modifySSHPolicyDisabled:
process.env.VUE_APP_MODIFY_SSH_POLICY_DISABLED === 'true',
sessionTimeOutOptions: [
- { value: 1800, text: i18n.global.t('pagePolicies.options.30minutes') },
- { value: 3600, text: i18n.global.t('pagePolicies.options.1hour') },
- { value: 7200, text: i18n.global.t('pagePolicies.options.2hours') },
- { value: 14400, text: i18n.global.t('pagePolicies.options.4hours') },
- { value: 28800, text: i18n.global.t('pagePolicies.options.8hours') },
- { value: 86400, text: i18n.global.t('pagePolicies.options.1day') },
+ {
+ value: 1800,
+ text: i18n.global.t('pagePolicies.options.30minutes'),
+ },
+ {
+ value: 3600,
+ text: i18n.global.t('pagePolicies.options.1hour'),
+ },
+ {
+ value: 7200,
+ text: i18n.global.t('pagePolicies.options.2hours'),
+ },
+ {
+ value: 14400,
+ text: i18n.global.t('pagePolicies.options.4hours'),
+ },
+ {
+ value: 28800,
+ text: i18n.global.t('pagePolicies.options.8hours'),
+ },
+ {
+ value: 86400,
+ text: i18n.global.t('pagePolicies.options.1day'),
+ },
],
};
},
@@ -246,9 +264,9 @@
.then((message) => this.successToast(message))
.catch(({ message }) => this.errorToast(message));
},
- saveSessionTimeoutValue(sessionTimeoutState) {
+ saveSessionTimeoutValue(value) {
this.$store
- .dispatch('policies/saveSessionTimeoutValue', sessionTimeoutState)
+ .dispatch('policies/saveSessionTimeoutValue', value)
.then((message) => this.successToast(message))
.catch(({ message }) => this.errorToast(message));
},
@@ -258,7 +276,7 @@
<style lang="scss" scoped>
.setting-section {
- border-bottom: 1px solid gray('300');
+ border-bottom: 1px solid $gray-300;
}
.session-timeout {
align-self: center;
diff --git a/src/views/SecurityAndAccess/Sessions/Sessions.vue b/src/views/SecurityAndAccess/Sessions/Sessions.vue
index 74dcf74..474ac95 100644
--- a/src/views/SecurityAndAccess/Sessions/Sessions.vue
+++ b/src/views/SecurityAndAccess/Sessions/Sessions.vue
@@ -21,7 +21,9 @@
<b-col>
<table-toolbar
ref="toolbar"
- :selected-items-count="selectedRows.length"
+ :selected-items-count="
+ Array.isArray(selectedRows) ? selectedRows.length : 0
+ "
:actions="batchActions"
@clear-selected="clearSelectedRows($refs.table)"
@batch-action="onBatchAction"
@@ -35,7 +37,8 @@
no-select-on-click
hover
show-empty
- sort-by="sessionID"
+ thead-class="table-light"
+ :sort-by="['sessionID']"
:busy="isBusy"
:fields="fields"
:items="allConnections"
@@ -44,7 +47,7 @@
:per-page="perPage"
:current-page="currentPage"
@filtered="onFiltered"
- @row-selected="onRowSelected($event, allConnections.length)"
+ @row-selected="onRowSelected"
>
<!-- Checkbox column -->
<template #head(checkbox)>
@@ -52,9 +55,11 @@
v-model="tableHeaderCheckboxModel"
data-test-id="sessions-checkbox-selectAll"
:indeterminate="tableHeaderCheckboxIndeterminate"
- @change="onChangeHeaderCheckbox($refs.table)"
+ @change="onChangeHeaderCheckbox($refs.table, $event)"
>
- <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectAll') }}
+ </span>
</b-form-checkbox>
</template>
<template #cell(checkbox)="row">
@@ -63,7 +68,9 @@
:data-test-id="`sessions-checkbox-selectRow-${row.index}`"
@change="toggleSelectRow($refs.table, row.index)"
>
- <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectItem') }}
+ </span>
</b-form-checkbox>
</template>
@@ -126,17 +133,14 @@
perPage,
itemsPerPageOptions,
} from '@/components/Mixins/BVPaginationMixin';
-import BVTableSelectableMixin, {
- selectedRows,
- tableHeaderCheckboxModel,
- tableHeaderCheckboxIndeterminate,
-} from '@/components/Mixins/BVTableSelectableMixin';
+import BVTableSelectableMixin from '@/components/Mixins/BVTableSelectableMixin';
import BVToastMixin from '@/components/Mixins/BVToastMixin';
import SearchFilterMixin, {
searchFilter,
} from '@/components/Mixins/SearchFilterMixin';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
components: {
@@ -159,6 +163,10 @@
this.hideLoader();
next();
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
@@ -203,10 +211,7 @@
currentPage: currentPage,
itemsPerPageOptions: itemsPerPageOptions,
perPage: perPage,
- selectedRows: selectedRows,
searchTotalFilteredRows: 0,
- tableHeaderCheckboxModel: tableHeaderCheckboxModel,
- tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate,
searchFilter: searchFilter,
};
},
@@ -259,47 +264,41 @@
},
onTableRowAction(action, { uri }) {
if (action === 'disconnect') {
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t('pageSessions.modal.disconnectMessage'),
- {
- title: i18n.global.t('pageSessions.modal.disconnectTitle'),
- okTitle: i18n.global.t('pageSessions.action.disconnect'),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfirmed) => {
- if (deleteConfirmed) this.disconnectSessions([uri]);
- });
+ this.confirmDialog(
+ i18n.global.t('pageSessions.modal.disconnectMessage'),
+ {
+ title: i18n.global.t('pageSessions.modal.disconnectTitle'),
+ okTitle: i18n.global.t('pageSessions.action.disconnect'),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ ).then((deleteConfirmed) => {
+ if (deleteConfirmed) this.disconnectSessions([uri]);
+ });
}
},
onBatchAction(action) {
if (action === 'disconnect') {
const uris = this.selectedRows.map((row) => row.uri);
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t(
- 'pageSessions.modal.disconnectMessage',
- this.selectedRows.length,
- ),
- {
- title: i18n.global.t(
- 'pageSessions.modal.disconnectTitle',
- this.selectedRows.length,
- ),
- okTitle: i18n.global.t('pageSessions.action.disconnect'),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfirmed) => {
- if (deleteConfirmed) {
- this.disconnectSessions(uris);
- }
- });
+ const count = this.selectedRows.length;
+ this.confirmDialog(
+ i18n.global.t('pageSessions.modal.disconnectMessage', count),
+ {
+ title: i18n.global.t('pageSessions.modal.disconnectTitle', count),
+ okTitle: i18n.global.t('pageSessions.action.disconnect'),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ ).then((deleteConfirmed) => {
+ if (deleteConfirmed) {
+ this.disconnectSessions(uris);
+ }
+ });
}
},
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
+ },
},
};
</script>
diff --git a/src/views/SecurityAndAccess/UserManagement/ModalSettings.vue b/src/views/SecurityAndAccess/UserManagement/ModalSettings.vue
index a181ba7..ed5114a 100644
--- a/src/views/SecurityAndAccess/UserManagement/ModalSettings.vue
+++ b/src/views/SecurityAndAccess/UserManagement/ModalSettings.vue
@@ -100,7 +100,7 @@
</b-row>
</b-container>
</b-form>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button
variant="secondary"
data-test-id="userManagement-button-cancel"
diff --git a/src/views/SecurityAndAccess/UserManagement/ModalUser.vue b/src/views/SecurityAndAccess/UserManagement/ModalUser.vue
index af8903e..e86a2ec 100644
--- a/src/views/SecurityAndAccess/UserManagement/ModalUser.vue
+++ b/src/views/SecurityAndAccess/UserManagement/ModalUser.vue
@@ -1,13 +1,5 @@
<template>
- <b-modal id="modal-user" ref="modal" @hidden="resetForm">
- <template #modal-title>
- <template v-if="newUser">
- {{ $t('pageUserManagement.addUser') }}
- </template>
- <template v-else>
- {{ $t('pageUserManagement.editUser') }}
- </template>
- </template>
+ <b-modal id="modal-user" ref="modal" :title="modalTitle" @hidden="resetForm">
<b-form id="form-user" novalidate @submit.prevent="handleSubmit">
<b-container>
<!-- Manual unlock form control -->
@@ -15,10 +7,12 @@
<b-col sm="9">
<alert :show="true" variant="warning" small>
<template v-if="!v$.form.manualUnlock.$dirty">
- {{ $t('pageUserManagement.modal.accountLocked') }}
+ {{ i18n.t('pageUserManagement.modal.accountLocked') }}
</template>
<template v-else>
- {{ $t('pageUserManagement.modal.clickSaveToUnlockAccount') }}
+ {{
+ i18n.t('pageUserManagement.modal.clickSaveToUnlockAccount')
+ }}
</template>
</alert>
</b-col>
@@ -34,13 +28,15 @@
data-test-id="userManagement-button-manualUnlock"
@click="v$.form.manualUnlock.$touch()"
>
- {{ $t('pageUserManagement.modal.unlock') }}
+ {{ i18n.t('pageUserManagement.modal.unlock') }}
</b-button>
</b-col>
</b-row>
<b-row>
<b-col>
- <b-form-group :label="$t('pageUserManagement.modal.accountStatus')">
+ <b-form-group
+ :label="i18n.t('pageUserManagement.modal.accountStatus')"
+ >
<b-form-radio
v-model="form.status"
name="user-status"
@@ -48,7 +44,7 @@
data-test-id="userManagement-radioButton-statusEnabled"
@input="v$.form.status.$touch()"
>
- {{ $t('global.status.enabled') }}
+ {{ i18n.t('global.status.enabled') }}
</b-form-radio>
<b-form-radio
v-model="form.status"
@@ -58,18 +54,18 @@
:disabled="!newUser && originalUsername === disabled"
@input="v$.form.status.$touch()"
>
- {{ $t('global.status.disabled') }}
+ {{ i18n.t('global.status.disabled') }}
</b-form-radio>
</b-form-group>
<b-form-group
- :label="$t('pageUserManagement.modal.username')"
+ :label="i18n.t('pageUserManagement.modal.username')"
label-for="username"
>
<b-form-text id="username-help-block">
- {{ $t('pageUserManagement.modal.cannotStartWithANumber') }}
+ {{ i18n.t('pageUserManagement.modal.cannotStartWithANumber') }}
<br />
{{
- $t(
+ i18n.t(
'pageUserManagement.modal.noSpecialCharactersExceptUnderscore',
)
}}
@@ -86,20 +82,23 @@
/>
<b-form-invalid-feedback role="alert">
<template v-if="v$.form.username.required.$invalid">
- {{ $t('global.form.fieldRequired') }}
+ {{ i18n.t('global.form.fieldRequired') }}
</template>
<template v-else-if="v$.form.username.maxLength.$invalid">
{{
- $t('global.form.lengthMustBeBetween', { min: 1, max: 16 })
+ i18n.t('global.form.lengthMustBeBetween', {
+ min: 1,
+ max: 16,
+ })
}}
</template>
<template v-else-if="v$.form.username.pattern.$invalid">
- {{ $t('global.form.invalidFormat') }}
+ {{ i18n.t('global.form.invalidFormat') }}
</template>
</b-form-invalid-feedback>
</b-form-group>
<b-form-group
- :label="$t('pageUserManagement.modal.privilege')"
+ :label="i18n.t('pageUserManagement.modal.privilege')"
label-for="privilege"
>
<b-form-select
@@ -113,25 +112,25 @@
>
<template #first>
<b-form-select-option :value="null" disabled>
- {{ $t('global.form.selectAnOption') }}
+ {{ i18n.t('global.form.selectAnOption') }}
</b-form-select-option>
</template>
</b-form-select>
<b-form-invalid-feedback role="alert">
<template v-if="v$.form.privilege.required.$invalid">
- {{ $t('global.form.fieldRequired') }}
+ {{ i18n.t('global.form.fieldRequired') }}
</template>
</b-form-invalid-feedback>
</b-form-group>
</b-col>
<b-col>
<b-form-group
- :label="$t('pageUserManagement.modal.userPassword')"
+ :label="i18n.t('pageUserManagement.modal.userPassword')"
label-for="password"
>
<b-form-text id="password-help-block">
{{
- $t('pageUserManagement.modal.passwordMustBeBetween', {
+ i18n.t('pageUserManagement.modal.passwordMustBeBetween', {
min: passwordRequirements.minLength,
max: passwordRequirements.maxLength,
})
@@ -142,6 +141,7 @@
id="password"
v-model="form.password"
type="password"
+ autocomplete="new-password"
data-test-id="userManagement-input-password"
aria-describedby="password-help-block"
:state="getValidationState(v$.form.password)"
@@ -150,7 +150,7 @@
/>
<b-form-invalid-feedback role="alert">
<template v-if="v$.form.password.required.$invalid">
- {{ $t('global.form.fieldRequired') }}
+ {{ i18n.t('global.form.fieldRequired') }}
</template>
<template
v-if="
@@ -159,7 +159,7 @@
"
>
{{
- $t('pageUserManagement.modal.passwordMustBeBetween', {
+ i18n.t('pageUserManagement.modal.passwordMustBeBetween', {
min: passwordRequirements.minLength,
max: passwordRequirements.maxLength,
})
@@ -169,7 +169,7 @@
</input-password-toggle>
</b-form-group>
<b-form-group
- :label="$t('pageUserManagement.modal.confirmUserPassword')"
+ :label="i18n.t('pageUserManagement.modal.confirmUserPassword')"
label-for="password-confirmation"
>
<input-password-toggle>
@@ -178,6 +178,7 @@
v-model="form.passwordConfirmation"
data-test-id="userManagement-input-passwordConfirmation"
type="password"
+ autocomplete="new-password"
:state="getValidationState(v$.form.passwordConfirmation)"
class="form-control-with-button"
@input="v$.form.passwordConfirmation.$touch()"
@@ -186,14 +187,14 @@
<template
v-if="v$.form.passwordConfirmation.required.$invalid"
>
- {{ $t('global.form.fieldRequired') }}
+ {{ i18n.t('global.form.fieldRequired') }}
</template>
<template
v-else-if="
v$.form.passwordConfirmation.sameAsPassword.$invalid
"
>
- {{ $t('pageUserManagement.modal.passwordsDoNotMatch') }}
+ {{ i18n.t('pageUserManagement.modal.passwordsDoNotMatch') }}
</template>
</b-form-invalid-feedback>
</input-password-toggle>
@@ -202,13 +203,13 @@
</b-row>
</b-container>
</b-form>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button
variant="secondary"
data-test-id="userManagement-button-cancel"
@click="cancel()"
>
- {{ $t('global.action.cancel') }}
+ {{ i18n.t('global.action.cancel') }}
</b-button>
<b-button
form="form-user"
@@ -218,10 +219,10 @@
@click="onOk"
>
<template v-if="newUser">
- {{ $t('pageUserManagement.addUser') }}
+ {{ i18n.t('pageUserManagement.addUser') }}
</template>
<template v-else>
- {{ $t('global.action.save') }}
+ {{ i18n.t('global.action.save') }}
</template>
</b-button>
</template>
@@ -258,13 +259,14 @@
},
emits: ['ok', 'hidden'],
setup() {
+ const i18n = useI18n();
return {
v$: useVuelidate(),
+ i18n,
};
},
data() {
return {
- $t: useI18n().t,
originalUsername: '',
form: {
status: true,
@@ -278,6 +280,11 @@
};
},
computed: {
+ modalTitle() {
+ return this.newUser
+ ? this.i18n.t('pageUserManagement.addUser')
+ : this.i18n.t('pageUserManagement.editUser');
+ },
newUser() {
return this.user ? false : true;
},
diff --git a/src/views/SecurityAndAccess/UserManagement/TableRoles.vue b/src/views/SecurityAndAccess/UserManagement/TableRoles.vue
index 7e9faef..03d0088 100644
--- a/src/views/SecurityAndAccess/UserManagement/TableRoles.vue
+++ b/src/views/SecurityAndAccess/UserManagement/TableRoles.vue
@@ -1,5 +1,12 @@
<template>
- <b-table stacked="sm" hover small :items="items" :fields="fields">
+ <b-table
+ stacked="sm"
+ hover
+ small
+ thead-class="table-light"
+ :items="items"
+ :fields="fields"
+ >
<template #cell(administrator)="data">
<template v-if="data.value">
<checkmark20 />
diff --git a/src/views/SecurityAndAccess/UserManagement/UserManagement.vue b/src/views/SecurityAndAccess/UserManagement/UserManagement.vue
index cb057a7..e59ffbc 100644
--- a/src/views/SecurityAndAccess/UserManagement/UserManagement.vue
+++ b/src/views/SecurityAndAccess/UserManagement/UserManagement.vue
@@ -2,7 +2,7 @@
<b-container fluid="xl">
<page-title />
<b-row>
- <b-col xl="9" class="text-right">
+ <b-col xl="9" class="text-end">
<b-button variant="link" @click="initModalSettings">
<icon-settings />
{{ $t('pageUserManagement.accountPolicySettings') }}
@@ -21,7 +21,9 @@
<b-col xl="9">
<table-toolbar
ref="toolbar"
- :selected-items-count="selectedRows.length"
+ :selected-items-count="
+ Array.isArray(selectedRows) ? selectedRows.length : 0
+ "
:actions="tableToolbarActions"
@clear-selected="clearSelectedRows($refs.table)"
@batch-action="onBatchAction"
@@ -33,6 +35,7 @@
show-empty
no-select-on-click
hover
+ thead-class="table-light"
:busy="isBusy"
:fields="fields"
:items="tableItems"
@@ -45,9 +48,11 @@
v-model="tableHeaderCheckboxModel"
data-test-id="userManagement-checkbox-tableHeaderCheckbox"
:indeterminate="tableHeaderCheckboxIndeterminate"
- @change="onChangeHeaderCheckbox($refs.table)"
+ @change="onChangeHeaderCheckbox($refs.table, $event)"
>
- <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectAll') }}
+ </span>
</b-form-checkbox>
</template>
<template #cell(checkbox)="row">
@@ -56,7 +61,9 @@
data-test-id="userManagement-checkbox-toggleSelectRow"
@change="toggleSelectRow($refs.table, row.index)"
>
- <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectItem') }}
+ </span>
</b-form-checkbox>
</template>
@@ -88,22 +95,27 @@
<b-row>
<b-col xl="8">
<b-button
- v-b-toggle.collapse-role-table
data-test-id="userManagement-button-viewPrivilegeRoleDescriptions"
variant="link"
class="mt-3"
+ @click="showRoles = !showRoles"
>
<icon-chevron />
{{ $t('pageUserManagement.viewPrivilegeRoleDescriptions') }}
</b-button>
- <b-collapse id="collapse-role-table" class="mt-3">
+ <b-collapse id="collapse-role-table" :visible="showRoles" class="mt-3">
<table-roles />
</b-collapse>
</b-col>
</b-row>
<!-- Modals -->
- <modal-settings :settings="setting" @ok="saveAccountSettings" />
+ <modal-settings
+ v-model="showSettingsModal"
+ :settings="setting"
+ @ok="saveAccountSettings"
+ />
<modal-user
+ v-model="showUserModal"
:user="activeUser"
:password-requirements="passwordRequirements"
@ok="saveUser"
@@ -135,6 +147,7 @@
import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'UserManagement',
@@ -156,6 +169,10 @@
this.hideLoader();
next();
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
@@ -181,7 +198,7 @@
{
key: 'actions',
label: '',
- tdClass: 'text-right text-nowrap',
+ tdClass: 'text-end text-nowrap',
},
],
tableToolbarActions: [
@@ -201,6 +218,9 @@
selectedRows: selectedRows,
tableHeaderCheckboxModel: tableHeaderCheckboxModel,
tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate,
+ showUserModal: false,
+ showSettingsModal: false,
+ showRoles: false,
};
},
computed: {
@@ -265,30 +285,28 @@
},
initModalUser(user) {
this.activeUser = user;
- this.$bvModal.show('modal-user');
+ this.showUserModal = true;
},
initModalDelete(user) {
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t('pageUserManagement.modal.deleteConfirmMessage', {
- user: user.username,
- }),
- {
- title: i18n.global.t('pageUserManagement.deleteUser'),
- okTitle: i18n.global.t('pageUserManagement.deleteUser'),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfirmed) => {
- if (deleteConfirmed) {
- this.deleteUser(user);
- }
- });
+ this.confirmDialog(
+ i18n.global.t('pageUserManagement.modal.deleteConfirmMessage', {
+ user: user.username,
+ }),
+ {
+ title: i18n.global.t('pageUserManagement.deleteUser'),
+ okTitle: i18n.global.t('pageUserManagement.deleteUser'),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ ).then((deleteConfirmed) => {
+ if (deleteConfirmed) {
+ this.deleteUser(user);
+ }
+ });
},
initModalSettings() {
this.setting = this.settings;
- this.$bvModal.show('modal-settings');
+ this.showSettingsModal = true;
},
saveUser({ isNewUser, userData }) {
this.startLoader();
@@ -315,41 +333,34 @@
.finally(() => this.endLoader());
},
onBatchAction(action) {
+ const count = this.selectedRows.length;
switch (action) {
case 'delete':
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t(
- 'pageUserManagement.modal.batchDeleteConfirmMessage',
- this.selectedRows.length,
- ),
- {
- title: i18n.global.t(
- 'pageUserManagement.deleteUser',
- this.selectedRows.length,
- ),
- okTitle: i18n.global.t(
- 'pageUserManagement.deleteUser',
- this.selectedRows.length,
- ),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfirmed) => {
- if (deleteConfirmed) {
- this.startLoader();
- this.$store
- .dispatch('userManagement/deleteUsers', this.selectedRows)
- .then((messages) => {
- messages.forEach(({ type, message }) => {
- if (type === 'success') this.successToast(message);
- if (type === 'error') this.errorToast(message);
- });
- })
- .finally(() => this.endLoader());
- }
- });
+ this.confirmDialog(
+ i18n.global.t(
+ 'pageUserManagement.modal.batchDeleteConfirmMessage',
+ count,
+ ),
+ {
+ title: i18n.global.t('pageUserManagement.deleteUser', count),
+ okTitle: i18n.global.t('pageUserManagement.deleteUser', count),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ ).then((deleteConfirmed) => {
+ if (deleteConfirmed) {
+ this.startLoader();
+ this.$store
+ .dispatch('userManagement/deleteUsers', this.selectedRows)
+ .then((messages) => {
+ messages.forEach(({ type, message }) => {
+ if (type === 'success') this.successToast(message);
+ if (type === 'error') this.errorToast(message);
+ });
+ })
+ .finally(() => this.endLoader());
+ }
+ });
break;
case 'enable':
this.startLoader();
@@ -364,42 +375,37 @@
.finally(() => this.endLoader());
break;
case 'disable':
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t(
- 'pageUserManagement.modal.batchDisableConfirmMessage',
- this.selectedRows.length,
- ),
- {
- title: i18n.global.t(
- 'pageUserManagement.disableUser',
- this.selectedRows.length,
- ),
- okTitle: i18n.global.t(
- 'pageUserManagement.disableUser',
- this.selectedRows.length,
- ),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((disableConfirmed) => {
- if (disableConfirmed) {
- this.startLoader();
- this.$store
- .dispatch('userManagement/disableUsers', this.selectedRows)
- .then((messages) => {
- messages.forEach(({ type, message }) => {
- if (type === 'success') this.successToast(message);
- if (type === 'error') this.errorToast(message);
- });
- })
- .finally(() => this.endLoader());
- }
- });
+ this.confirmDialog(
+ i18n.global.t(
+ 'pageUserManagement.modal.batchDisableConfirmMessage',
+ count,
+ ),
+ {
+ title: i18n.global.t('pageUserManagement.disableUser', count),
+ okTitle: i18n.global.t('pageUserManagement.disableUser', count),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ ).then((disableConfirmed) => {
+ if (disableConfirmed) {
+ this.startLoader();
+ this.$store
+ .dispatch('userManagement/disableUsers', this.selectedRows)
+ .then((messages) => {
+ messages.forEach(({ type, message }) => {
+ if (type === 'success') this.successToast(message);
+ if (type === 'error') this.errorToast(message);
+ });
+ })
+ .finally(() => this.endLoader());
+ }
+ });
break;
}
},
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
+ },
onTableRowAction(action, row) {
switch (action) {
case 'edit':
diff --git a/src/views/Settings/DateTime/DateTime.vue b/src/views/Settings/DateTime/DateTime.vue
index 0ddea67..7be9d18 100644
--- a/src/views/Settings/DateTime/DateTime.vue
+++ b/src/views/Settings/DateTime/DateTime.vue
@@ -18,14 +18,18 @@
<b-col lg="3">
<dl>
<dt>{{ $t('pageDateTime.form.date') }}</dt>
- <dd v-if="bmcTime">{{ $filters.formatDate(bmcTime) }}</dd>
+ <dd v-if="bmcTime">
+ {{ $filters.formatDate(bmcTime) }}
+ </dd>
<dd v-else>--</dd>
</dl>
</b-col>
<b-col lg="3">
<dl>
<dt>{{ $t('pageDateTime.form.time.label') }}</dt>
- <dd v-if="bmcTime">{{ $filters.formatTime(bmcTime) }}</dd>
+ <dd v-if="bmcTime">
+ {{ $filters.formatTime(bmcTime) }}
+ </dd>
<dd v-else>--</dd>
</dl>
</b-col>
@@ -36,7 +40,7 @@
<b-form-group
label="Configure date and time"
:disabled="loading"
- label-sr-only
+ label-class="visually-hidden"
>
<b-form-radio
v-model="form.configurationSelected"
@@ -45,7 +49,7 @@
>
{{ $t('pageDateTime.form.manual') }}
</b-form-radio>
- <b-row class="mt-3 ml-3">
+ <b-row class="mt-3 ms-3">
<b-col sm="6" lg="4" xl="3">
<b-form-group
:label="$t('pageDateTime.form.date')"
@@ -70,34 +74,16 @@
{{ $t('global.form.fieldRequired') }}
</div>
</b-form-invalid-feedback>
- <b-form-datepicker
- v-model="form.manual.date"
- class="btn-datepicker btn-icon-only"
- button-only
- right
- :hide-header="true"
- :locale="locale"
- :label-help="
- $t('global.calendar.useCursorKeysToNavigateCalendarDates')
- "
- :title="$t('global.calendar.selectDate')"
- :disabled="ntpOptionSelected"
- button-variant="link"
- aria-controls="input-manual-date"
- >
- <template #button-content>
- <icon-calendar />
- <span class="sr-only">
- {{ $t('global.calendar.selectDate') }}
- </span>
- </template>
- </b-form-datepicker>
</b-input-group>
</b-form-group>
</b-col>
<b-col sm="6" lg="4" xl="3">
<b-form-group
- :label="$t('pageDateTime.form.time.timezone', { timezone })"
+ :label="
+ $t('pageDateTime.form.time.timezone', {
+ timezone,
+ })
+ "
label-for="input-manual-time"
>
<b-form-text id="time-format-help">HH:MM</b-form-text>
@@ -129,7 +115,7 @@
>
NTP
</b-form-radio>
- <b-row class="mt-3 ml-3">
+ <b-row class="mt-3 ms-3">
<b-col sm="6" lg="4" xl="3">
<b-form-group
:label="$t('pageDateTime.form.ntpServers.server1')"
@@ -198,7 +184,6 @@
<script>
import Alert from '@/components/Global/Alert';
-import IconCalendar from '@carbon/icons-vue/es/calendar/20';
import PageTitle from '@/components/Global/PageTitle';
import PageSection from '@/components/Global/PageSection';
@@ -218,7 +203,7 @@
export default {
name: 'DateTime',
- components: { Alert, IconCalendar, PageTitle, PageSection },
+ components: { Alert, PageTitle, PageSection },
mixins: [
BVToastMixin,
LoadingBarMixin,
diff --git a/src/views/Settings/Network/ModalDefaultGateway.vue b/src/views/Settings/Network/ModalDefaultGateway.vue
index d1fa60f..1ac6f14 100644
--- a/src/views/Settings/Network/ModalDefaultGateway.vue
+++ b/src/views/Settings/Network/ModalDefaultGateway.vue
@@ -32,7 +32,7 @@
</b-col>
</b-row>
</b-form>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button variant="secondary" @click="cancel()">
{{ $t('global.action.cancel') }}
</b-button>
@@ -63,12 +63,16 @@
export default {
mixins: [VuelidateMixin],
props: {
+ modelValue: {
+ type: Boolean,
+ default: false,
+ },
defaultGateway: {
type: String,
default: '',
},
},
- emits: ['ok', 'hidden'],
+ emits: ['ok', 'hidden', 'update:modelValue'],
setup() {
return {
v$: useVuelidate(),
@@ -86,6 +90,16 @@
defaultGateway() {
this.form.defaultGateway = this.defaultGateway;
},
+ modelValue: {
+ handler(newValue) {
+ if (newValue) {
+ this.$nextTick(() => {
+ this.$refs.modal?.show();
+ });
+ }
+ },
+ immediate: true,
+ },
},
validations() {
return {
@@ -112,6 +126,7 @@
resetForm() {
this.form.defaultGateway = this.defaultGateway;
this.v$.$reset();
+ this.$emit('update:modelValue', false);
this.$emit('hidden');
},
onOk(bvModalEvt) {
diff --git a/src/views/Settings/Network/ModalDns.vue b/src/views/Settings/Network/ModalDns.vue
index 39308ce..d9e9830 100644
--- a/src/views/Settings/Network/ModalDns.vue
+++ b/src/views/Settings/Network/ModalDns.vue
@@ -31,7 +31,7 @@
</b-col>
</b-row>
</b-form>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button variant="secondary" @click="cancel()">
{{ $t('global.action.cancel') }}
</b-button>
@@ -51,7 +51,13 @@
export default {
mixins: [VuelidateMixin],
- emits: ['ok', 'hidden'],
+ props: {
+ modelValue: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ emits: ['ok', 'hidden', 'update:modelValue'],
setup() {
return {
v$: useVuelidate(),
@@ -75,6 +81,18 @@
},
};
},
+ watch: {
+ modelValue: {
+ handler(newValue) {
+ if (newValue) {
+ this.$nextTick(() => {
+ this.$refs.modal?.show();
+ });
+ }
+ },
+ immediate: true,
+ },
+ },
methods: {
handleSubmit() {
this.v$.$touch();
@@ -90,6 +108,7 @@
resetForm() {
this.form.staticDns = null;
this.v$.$reset();
+ this.$emit('update:modelValue', false);
this.$emit('hidden');
},
onOk(bvModalEvt) {
diff --git a/src/views/Settings/Network/ModalHostname.vue b/src/views/Settings/Network/ModalHostname.vue
index eb20f17..19ef61c 100644
--- a/src/views/Settings/Network/ModalHostname.vue
+++ b/src/views/Settings/Network/ModalHostname.vue
@@ -31,7 +31,7 @@
</b-col>
</b-row>
</b-form>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button variant="secondary" @click="cancel()">
{{ $t('global.action.cancel') }}
</b-button>
@@ -59,12 +59,16 @@
export default {
mixins: [VuelidateMixin],
props: {
+ modelValue: {
+ type: Boolean,
+ default: false,
+ },
hostname: {
type: String,
default: '',
},
},
- emits: ['ok', 'hidden'],
+ emits: ['ok', 'hidden', 'update:modelValue'],
setup() {
return {
v$: useVuelidate(),
@@ -82,6 +86,16 @@
hostname() {
this.form.hostname = this.hostname;
},
+ modelValue: {
+ handler(newValue) {
+ if (newValue) {
+ this.$nextTick(() => {
+ this.$refs.modal?.show();
+ });
+ }
+ },
+ immediate: true,
+ },
},
validations() {
return {
@@ -108,6 +122,7 @@
resetForm() {
this.form.hostname = this.hostname;
this.v$.$reset();
+ this.$emit('update:modelValue', false);
this.$emit('hidden');
},
onOk(bvModalEvt) {
diff --git a/src/views/Settings/Network/ModalIpv4.vue b/src/views/Settings/Network/ModalIpv4.vue
index e72179a..45bd411 100644
--- a/src/views/Settings/Network/ModalIpv4.vue
+++ b/src/views/Settings/Network/ModalIpv4.vue
@@ -77,7 +77,7 @@
</b-col>
</b-row>
</b-form>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button variant="secondary" @click="cancel()">
{{ $t('global.action.cancel') }}
</b-button>
@@ -98,12 +98,16 @@
export default {
mixins: [VuelidateMixin],
props: {
+ modelValue: {
+ type: Boolean,
+ default: false,
+ },
defaultGateway: {
type: String,
default: '',
},
},
- emits: ['ok', 'hidden'],
+ emits: ['ok', 'hidden', 'update:modelValue'],
setup() {
return {
v$: useVuelidate(),
@@ -123,6 +127,16 @@
defaultGateway() {
this.form.gateway = this.defaultGateway;
},
+ modelValue: {
+ handler(newValue) {
+ if (newValue) {
+ this.$nextTick(() => {
+ this.$refs.modal?.show();
+ });
+ }
+ },
+ immediate: true,
+ },
},
validations() {
return {
@@ -143,6 +157,12 @@
};
},
methods: {
+ show() {
+ this.$refs.modal?.show();
+ },
+ hide() {
+ this.$refs.modal?.hide();
+ },
handleSubmit() {
this.v$.$touch();
if (this.v$.$invalid) return;
@@ -163,6 +183,7 @@
this.form.gateway = this.defaultGateway;
this.form.subnetMask = null;
this.v$.$reset();
+ this.$emit('update:modelValue', false);
this.$emit('hidden');
},
onOk(bvModalEvt) {
diff --git a/src/views/Settings/Network/ModalIpv6.vue b/src/views/Settings/Network/ModalIpv6.vue
index 6f844ce..358f34f 100644
--- a/src/views/Settings/Network/ModalIpv6.vue
+++ b/src/views/Settings/Network/ModalIpv6.vue
@@ -55,7 +55,7 @@
</b-col>
</b-row>
</b-form>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button variant="secondary" @click="cancel()">
{{ $t('global.action.cancel') }}
</b-button>
@@ -85,7 +85,13 @@
export default {
mixins: [VuelidateMixin],
- emits: ['ok', 'hidden'],
+ props: {
+ modelValue: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ emits: ['ok', 'hidden', 'update:modelValue'],
setup() {
return {
v$: useVuelidate(),
@@ -114,6 +120,18 @@
},
};
},
+ watch: {
+ modelValue: {
+ handler(newValue) {
+ if (newValue) {
+ this.$nextTick(() => {
+ this.$refs.modal?.show();
+ });
+ }
+ },
+ immediate: true,
+ },
+ },
methods: {
handleSubmit() {
this.v$.$touch();
@@ -133,6 +151,7 @@
this.form.ipAddress = null;
this.form.prefixLength = null;
this.v$.$reset();
+ this.$emit('update:modelValue', false);
this.$emit('hidden');
},
onOk(bvModalEvt) {
diff --git a/src/views/Settings/Network/ModalMacAddress.vue b/src/views/Settings/Network/ModalMacAddress.vue
index 83c1406..2d70ce6 100644
--- a/src/views/Settings/Network/ModalMacAddress.vue
+++ b/src/views/Settings/Network/ModalMacAddress.vue
@@ -32,7 +32,7 @@
</b-col>
</b-row>
</b-form>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button variant="secondary" @click="cancel()">
{{ $t('global.action.cancel') }}
</b-button>
@@ -58,12 +58,16 @@
export default {
mixins: [VuelidateMixin],
props: {
+ modelValue: {
+ type: Boolean,
+ default: false,
+ },
macAddress: {
type: String,
default: '',
},
},
- emits: ['ok', 'hidden'],
+ emits: ['ok', 'hidden', 'update:modelValue'],
setup() {
return {
v$: useVuelidate(),
@@ -81,6 +85,16 @@
macAddress() {
this.form.macAddress = this.macAddress;
},
+ modelValue: {
+ handler(newValue) {
+ if (newValue) {
+ this.$nextTick(() => {
+ this.$refs.modal?.show();
+ });
+ }
+ },
+ immediate: true,
+ },
},
validations() {
return {
@@ -107,6 +121,7 @@
resetForm() {
this.form.macAddress = this.macAddress;
this.v$.$reset();
+ this.$emit('update:modelValue', false);
this.$emit('hidden');
},
onOk(bvModalEvt) {
diff --git a/src/views/Settings/Network/Network.vue b/src/views/Settings/Network/Network.vue
index 7a2e014..73118c8 100644
--- a/src/views/Settings/Network/Network.vue
+++ b/src/views/Settings/Network/Network.vue
@@ -4,20 +4,22 @@
<!-- Global settings for all interfaces -->
<network-global-settings />
<!-- Interface tabs -->
- <page-section v-show="ethernetData">
+ <page-section v-if="ethernetData && ethernetData.length">
<b-row>
<b-col>
<b-card no-body>
<b-tabs
- active-nav-item-class="font-weight-bold"
+ :key="tabsRenderKey"
+ v-model:index="tabIndex"
+ active-nav-item-class="fw-bold"
card
content-class="mt-3"
+ :lazy="false"
>
<b-tab
- v-for="(data, index) in ethernetData"
+ v-for="data in ethernetData"
:key="data.Id"
:title="data.Id"
- @click="getTabIndex(index)"
>
<!-- Interface settings -->
<network-interface-settings :tab-index="tabIndex" />
@@ -37,9 +39,18 @@
<modal-ipv4 :default-gateway="defaultGateway" @ok="saveIpv4Address" />
<modal-ipv6 @ok="saveIpv6Address" />
<modal-dns @ok="saveDnsAddress" />
- <modal-hostname :hostname="currentHostname" @ok="saveSettings" />
- <modal-mac-address :mac-address="currentMacAddress" @ok="saveSettings" />
+ <modal-hostname
+ v-model="showHostnameModal"
+ :hostname="currentHostname"
+ @ok="saveSettings"
+ />
+ <modal-mac-address
+ v-model="showMacAddressModal"
+ :mac-address="currentMacAddress"
+ @ok="saveSettings"
+ />
<modal-default-gateway
+ v-model="showDefaultGatewayModal"
:default-gateway="ipv6DefaultGateway"
@ok="saveSettings"
/>
@@ -97,6 +108,11 @@
ipv6DefaultGateway: '',
loading,
tabIndex: 0,
+ tabsReady: false,
+ tabsRenderKey: 0,
+ showHostnameModal: false,
+ showDefaultGatewayModal: false,
+ showMacAddressModal: false,
};
},
computed: {
@@ -106,23 +122,32 @@
ethernetData() {
this.getModalInfo();
},
+ tabIndex(newIndex) {
+ this.$store.dispatch('network/setSelectedTabIndex', newIndex);
+ this.$store.dispatch(
+ 'network/setSelectedTabId',
+ this.ethernetData?.[newIndex]?.Id,
+ );
+ this.getModalInfo();
+ },
},
created() {
this.startLoader();
+ const eventBus = require('@/eventBus').default;
const globalSettings = new Promise((resolve) => {
- this.$root.$on('network-global-settings-complete', () => resolve());
+ eventBus.$once('network-global-settings-complete', resolve);
});
const interfaceSettings = new Promise((resolve) => {
- this.$root.$on('network-interface-settings-complete', () => resolve());
+ eventBus.$once('network-interface-settings-complete', resolve);
});
const networkTableDns = new Promise((resolve) => {
- this.$root.$on('network-table-dns-complete', () => resolve());
+ eventBus.$once('network-table-dns-complete', resolve);
});
const networkTableIpv4 = new Promise((resolve) => {
- this.$root.$on('network-table-ipv4-complete', () => resolve());
+ eventBus.$once('network-table-ipv4-complete', resolve);
});
const networkTableIpv6 = new Promise((resolve) => {
- this.$root.$on('network-table-ipv6-complete', () => resolve());
+ eventBus.$once('network-table-ipv6-complete', resolve);
});
// Combine all child component Promises to indicate
// when page data load complete
@@ -133,28 +158,36 @@
networkTableDns,
networkTableIpv4,
networkTableIpv6,
- ]).finally(() => this.endLoader());
+ ])
+ .then(() => {
+ // ensure first tab is selected and expanded (index 0). Force a change
+ // cycle to trigger BTabs to render the pane content immediately.
+ const count = this.ethernetData?.length || 0;
+ if (count > 0) {
+ // set initial selection directly to index 0
+ this.tabIndex = 0;
+ this.$store.dispatch('network/setSelectedTabIndex', 0);
+ const firstId = this.ethernetData?.[0]?.Id;
+ if (firstId)
+ this.$store.dispatch('network/setSelectedTabId', firstId);
+ this.tabsRenderKey += 1;
+ }
+ })
+ .finally(() => this.endLoader());
},
methods: {
getModalInfo() {
- this.defaultGateway =
- this.$store.getters['network/globalNetworkSettings'][
- this.tabIndex
- ].defaultGateway;
+ const settingsArray =
+ this.$store.getters['network/globalNetworkSettings'];
+ const settings = Array.isArray(settingsArray)
+ ? settingsArray[this.tabIndex]
+ : undefined;
- this.currentHostname =
- this.$store.getters['network/globalNetworkSettings'][
- this.tabIndex
- ].hostname;
-
- this.currentMacAddress =
- this.$store.getters['network/globalNetworkSettings'][
- this.tabIndex
- ].macAddress;
- this.ipv6DefaultGateway =
- this.$store.getters['network/globalNetworkSettings'][
- this.tabIndex
- ].ipv6DefaultGateway;
+ if (!settings) return;
+ this.defaultGateway = settings.defaultGateway;
+ this.currentHostname = settings.hostname;
+ this.currentMacAddress = settings.macAddress;
+ this.ipv6DefaultGateway = settings.ipv6DefaultGateway;
},
getTabIndex(selectedIndex) {
this.tabIndex = selectedIndex;
diff --git a/src/views/Settings/Network/NetworkGlobalSettings.vue b/src/views/Settings/Network/NetworkGlobalSettings.vue
index 23ce6ca..4f11421 100644
--- a/src/views/Settings/Network/NetworkGlobalSettings.vue
+++ b/src/views/Settings/Network/NetworkGlobalSettings.vue
@@ -134,16 +134,22 @@
import PageSection from '@/components/Global/PageSection';
import { mapState } from 'vuex';
import { useI18n } from 'vue-i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'GlobalNetworkSettings',
components: { IconEdit, PageSection },
mixins: [BVToastMixin, DataFormatterMixin],
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
hostname: '',
+ showHostnameModal: false,
};
},
computed: {
@@ -209,14 +215,14 @@
created() {
this.$store.dispatch('network/getEthernetData').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('network-global-settings-complete');
+ require('@/eventBus').default.$emit('network-global-settings-complete');
});
},
methods: {
changeDomainNameState(state) {
this.$store
.dispatch('network/saveDomainNameState', {
- domainState: state,
+ domainState: !!state,
ipVersion: 'IPv4',
})
.then((success) => {
@@ -227,7 +233,7 @@
changeDnsState(state) {
this.$store
.dispatch('network/saveDnsState', {
- dnsState: state,
+ dnsState: !!state,
ipVersion: 'IPv4',
})
.then((message) => {
@@ -238,7 +244,7 @@
changeNtpState(state) {
this.$store
.dispatch('network/saveNtpState', {
- ntpState: state,
+ ntpState: !!state,
ipVersion: 'IPv4',
})
.then((message) => {
@@ -249,7 +255,7 @@
changeDomainNameStateIpv6(state) {
this.$store
.dispatch('network/saveDomainNameState', {
- domainState: state,
+ domainState: !!state,
ipVersion: 'IPv6',
})
.then((success) => {
@@ -260,7 +266,7 @@
changeDnsStateIpv6(state) {
this.$store
.dispatch('network/saveDnsState', {
- dnsState: state,
+ dnsState: !!state,
ipVersion: 'IPv6',
})
.then((message) => {
@@ -271,7 +277,7 @@
changeNtpStateIpv6(state) {
this.$store
.dispatch('network/saveNtpState', {
- ntpState: state,
+ ntpState: !!state,
ipVersion: 'IPv6',
})
.then((message) => {
@@ -280,7 +286,7 @@
.catch(({ message }) => this.errorToast(message));
},
initSettingsModal() {
- this.$bvModal.show('modal-hostname');
+ this.showHostnameModal = true;
},
},
};
diff --git a/src/views/Settings/Network/NetworkInterfaceSettings.vue b/src/views/Settings/Network/NetworkInterfaceSettings.vue
index ea83757..18f4868 100644
--- a/src/views/Settings/Network/NetworkInterfaceSettings.vue
+++ b/src/views/Settings/Network/NetworkInterfaceSettings.vue
@@ -63,6 +63,7 @@
import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin';
import { mapState } from 'vuex';
import { useI18n } from 'vue-i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'Ipv4Table',
@@ -77,6 +78,10 @@
default: 0,
},
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
@@ -85,6 +90,7 @@
linkSpeed: '',
fqdn: '',
macAddress: '',
+ showMacAddressModal: false,
};
},
computed: {
@@ -100,7 +106,9 @@
this.getSettings();
this.$store.dispatch('network/getEthernetData').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('network-interface-settings-complete');
+ require('@/eventBus').default.$emit(
+ 'network-interface-settings-complete',
+ );
});
},
methods: {
@@ -112,7 +120,7 @@
this.macAddress = this.ethernetData[this.selectedInterface].MACAddress;
},
initMacAddressModal() {
- this.$bvModal.show('modal-mac-address');
+ this.showMacAddressModal = true;
},
},
};
diff --git a/src/views/Settings/Network/TableDns.vue b/src/views/Settings/Network/TableDns.vue
index b0e5d80..e78234b 100644
--- a/src/views/Settings/Network/TableDns.vue
+++ b/src/views/Settings/Network/TableDns.vue
@@ -2,7 +2,7 @@
<page-section :section-title="$t('pageNetwork.staticDns')">
<b-row>
<b-col lg="6">
- <div class="text-right">
+ <div class="text-end">
<b-button variant="primary" @click="initDnsModal()">
<icon-add />
{{ $t('pageNetwork.table.addDnsAddress') }}
@@ -11,6 +11,7 @@
<b-table
responsive="md"
hover
+ thead-class="table-light"
:fields="dnsTableFields"
:items="form.dnsStaticTableItems"
:empty-text="$t('global.table.emptyMessage')"
@@ -36,6 +37,7 @@
</b-col>
</b-row>
</page-section>
+ <modal-dns v-model="showDnsModal" />
</template>
<script>
@@ -45,9 +47,11 @@
import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
import PageSection from '@/components/Global/PageSection';
import TableRowAction from '@/components/Global/TableRowAction';
+import ModalDns from './ModalDns.vue';
import { mapState } from 'vuex';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'DNSTable',
@@ -57,6 +61,7 @@
IconTrashcan,
PageSection,
TableRowAction,
+ ModalDns,
},
mixins: [BVToastMixin],
props: {
@@ -65,12 +70,17 @@
default: 0,
},
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
form: {
dnsStaticTableItems: [],
},
+ showDnsModal: false,
actions: [
{
value: 'edit',
@@ -86,7 +96,7 @@
key: 'address',
label: i18n.global.t('pageNetwork.table.ipAddress'),
},
- { key: 'actions', label: '', tdClass: 'text-right' },
+ { key: 'actions', label: '', tdClass: 'text-end' },
],
};
},
@@ -106,7 +116,7 @@
this.getStaticDnsItems();
this.$store.dispatch('network/getEthernetData').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('network-table-dns-complete');
+ require('@/eventBus').default.$emit('network-table-dns-complete');
});
},
methods: {
@@ -141,7 +151,7 @@
.catch(({ message }) => this.errorToast(message));
},
initDnsModal() {
- this.$bvModal.show('modal-dns');
+ this.showDnsModal = true;
},
},
};
diff --git a/src/views/Settings/Network/TableIpv4.vue b/src/views/Settings/Network/TableIpv4.vue
index b95e7d3..994990c 100644
--- a/src/views/Settings/Network/TableIpv4.vue
+++ b/src/views/Settings/Network/TableIpv4.vue
@@ -27,7 +27,7 @@
{{ $t('pageNetwork.ipv4Addresses') }}
</h3>
</b-col>
- <b-col class="text-right">
+ <b-col class="text-end">
<b-button variant="primary" @click="initAddIpv4Address()">
<icon-add />
{{ $t('pageNetwork.table.addIpv4Address') }}
@@ -37,6 +37,7 @@
<b-table
responsive="md"
hover
+ thead-class="table-light"
:fields="ipv4TableFields"
:items="form.ipv4TableItems"
:empty-text="$t('global.table.emptyMessage')"
@@ -59,6 +60,7 @@
</table-row-action>
</template>
</b-table>
+ <modal-ipv4 v-model="showAddIpv4" />
</page-section>
</template>
@@ -70,9 +72,11 @@
import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
import PageSection from '@/components/Global/PageSection';
import TableRowAction from '@/components/Global/TableRowAction';
+import ModalIpv4 from './ModalIpv4.vue';
import { mapState } from 'vuex';
import { useI18n } from 'vue-i18n';
import i18n from '@/i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'Ipv4Table',
@@ -82,6 +86,7 @@
IconTrashcan,
PageSection,
TableRowAction,
+ ModalIpv4,
},
mixins: [BVToastMixin, LoadingBarMixin],
props: {
@@ -90,9 +95,14 @@
default: 0,
},
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
+ showAddIpv4: false,
form: {
ipv4TableItems: [],
},
@@ -123,7 +133,7 @@
key: 'AddressOrigin',
label: i18n.global.t('pageNetwork.table.addressOrigin'),
},
- { key: 'actions', label: '', tdClass: 'text-right' },
+ { key: 'actions', label: '', tdClass: 'text-end' },
],
};
},
@@ -165,7 +175,7 @@
this.getIpv4TableItems();
this.$store.dispatch('network/getEthernetData').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('network-table-ipv4-complete');
+ require('@/eventBus').default.$emit('network-table-ipv4-complete');
});
},
methods: {
@@ -208,39 +218,39 @@
.catch(({ message }) => this.errorToast(message));
},
initAddIpv4Address() {
- this.$bvModal.show('modal-add-ipv4');
+ this.showAddIpv4 = true;
},
changeDhcpEnabledState(state) {
- this.$bvModal
- .msgBoxConfirm(
- state
- ? i18n.global.t('pageNetwork.modal.confirmEnableDhcp')
- : i18n.global.t('pageNetwork.modal.confirmDisableDhcp'),
- {
- title: i18n.global.t('pageNetwork.modal.dhcpConfirmTitle', {
- dhcpState: state
- ? i18n.global.t('global.action.enable')
- : i18n.global.t('global.action.disable'),
- }),
- okTitle: state
- ? i18n.global.t('global.action.enable')
- : i18n.global.t('global.action.disable'),
- okVariant: 'danger',
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'cancel',
- },
- )
- .then((dhcpEnableConfirmed) => {
- if (dhcpEnableConfirmed) {
- this.$store
- .dispatch('network/saveDhcpEnabledState', state)
- .then((message) => this.successToast(message))
- .catch(({ message }) => this.errorToast(message));
- } else {
- let onDhcpCancel = document.getElementById('dhcpSwitch');
- onDhcpCancel.checked = !state;
- }
- });
+ const dhcpState = state
+ ? i18n.global.t('global.action.enable')
+ : i18n.global.t('global.action.disable');
+ this.confirmDialog(
+ state
+ ? i18n.global.t('pageNetwork.modal.confirmEnableDhcp')
+ : i18n.global.t('pageNetwork.modal.confirmDisableDhcp'),
+ {
+ title: i18n.global.t('pageNetwork.modal.dhcpConfirmTitle', {
+ dhcpState,
+ }),
+ okTitle: dhcpState,
+ okVariant: 'danger',
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'cancel',
+ },
+ ).then((dhcpEnableConfirmed) => {
+ if (dhcpEnableConfirmed) {
+ this.$store
+ .dispatch('network/saveDhcpEnabledState', state)
+ .then((message) => this.successToast(message))
+ .catch(({ message }) => this.errorToast(message));
+ } else {
+ let onDhcpCancel = document.getElementById('dhcpSwitch');
+ onDhcpCancel.checked = !state;
+ }
+ });
+ },
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
},
},
};
diff --git a/src/views/Settings/Network/TableIpv6.vue b/src/views/Settings/Network/TableIpv6.vue
index bdebc27..bf2e6e1 100644
--- a/src/views/Settings/Network/TableIpv6.vue
+++ b/src/views/Settings/Network/TableIpv6.vue
@@ -47,7 +47,7 @@
{{ $t('pageNetwork.ipv6Addresses') }}
</h3>
</b-col>
- <b-col class="text-right">
+ <b-col class="text-end">
<b-button variant="primary" @click="initAddIpv6Address()">
<icon-add />
{{ $t('pageNetwork.table.addIpv6Address') }}
@@ -57,6 +57,7 @@
<b-table
responsive="md"
hover
+ thead-class="table-light"
:fields="ipv6TableFields"
:items="form.ipv6TableItems"
:empty-text="$t('global.table.emptyMessage')"
@@ -79,6 +80,7 @@
</table-row-action>
</template>
</b-table>
+ <modal-ipv6 v-model="showAddIpv6" />
</page-section>
</template>
@@ -91,9 +93,11 @@
import PageSection from '@/components/Global/PageSection';
import TableRowAction from '@/components/Global/TableRowAction';
import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin';
+import ModalIpv6 from './ModalIpv6.vue';
import { mapState } from 'vuex';
import i18n from '@/i18n';
import { useI18n } from 'vue-i18n';
+import { useModal } from 'bootstrap-vue-next';
export default {
name: 'Ipv6Table',
@@ -103,6 +107,7 @@
IconTrashcan,
PageSection,
TableRowAction,
+ ModalIpv6,
},
mixins: [BVToastMixin, LoadingBarMixin, DataFormatterMixin],
props: {
@@ -111,9 +116,15 @@
default: 0,
},
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
+ showAddIpv6: false,
+ showDefaultGatewayModal: false,
form: {
ipv6TableItems: [],
},
@@ -140,7 +151,7 @@
key: 'AddressOrigin',
label: i18n.global.t('pageNetwork.table.addressOrigin'),
},
- { key: 'actions', label: '', tdClass: 'text-right' },
+ { key: 'actions', label: '', tdClass: 'text-end' },
],
defaultGateway: '',
defaultGatewayEditable:
@@ -190,7 +201,7 @@
this.getDefaultGateway();
this.$store.dispatch('network/getEthernetData').finally(() => {
// Emit initial data fetch complete to parent component
- this.$root.$emit('network-table-ipv6-complete');
+ require('@/eventBus').default.$emit('network-table-ipv6-complete');
});
},
methods: {
@@ -251,41 +262,42 @@
.catch(({ message }) => this.errorToast(message));
},
initAddIpv6Address() {
- this.$bvModal.show('modal-add-ipv6');
+ this.showAddIpv6 = true;
},
changeDhcp6EnabledState(state) {
- this.$bvModal
- .msgBoxConfirm(
- state
- ? i18n.global.t('pageNetwork.modal.confirmEnableDhcp')
- : i18n.global.t('pageNetwork.modal.confirmDisableDhcp'),
- {
- title: i18n.global.t('pageNetwork.modal.dhcpConfirmTitle', {
- dhcpState: state
- ? i18n.global.t('global.action.enable')
- : i18n.global.t('global.action.disable'),
- }),
- okTitle: state
- ? i18n.global.t('global.action.enable')
- : i18n.global.t('global.action.disable'),
- okVariant: 'danger',
- cancelTitle: i18n.global.t('global.action.cancel'),
- },
- )
- .then((dhcpEnableConfirmed) => {
- if (dhcpEnableConfirmed) {
- this.$store
- .dispatch('network/saveDhcp6EnabledState', state)
- .then((message) => this.successToast(message))
- .catch(({ message }) => this.errorToast(message));
- } else {
- let onDhcpCancel = document.getElementById('dhcp6Switch');
- onDhcpCancel.checked = !state;
- }
- });
+ const dhcpState = state
+ ? i18n.global.t('global.action.enable')
+ : i18n.global.t('global.action.disable');
+ this.confirmDialog(
+ state
+ ? i18n.global.t('pageNetwork.modal.confirmEnableDhcp')
+ : i18n.global.t('pageNetwork.modal.confirmDisableDhcp'),
+ {
+ title: i18n.global.t('pageNetwork.modal.dhcpConfirmTitle', {
+ dhcpState,
+ }),
+ okTitle: dhcpState,
+ okVariant: 'danger',
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'cancel',
+ },
+ ).then((dhcpEnableConfirmed) => {
+ if (dhcpEnableConfirmed) {
+ this.$store
+ .dispatch('network/saveDhcp6EnabledState', state)
+ .then((message) => this.successToast(message))
+ .catch(({ message }) => this.errorToast(message));
+ } else {
+ let onDhcpCancel = document.getElementById('dhcp6Switch');
+ onDhcpCancel.checked = !state;
+ }
+ });
+ },
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
},
initDefaultGatewayModal() {
- this.$bvModal.show('modal-default-gateway');
+ this.showDefaultGatewayModal = true;
},
},
};
diff --git a/src/views/Settings/SnmpAlerts/ModalAddDestination.vue b/src/views/Settings/SnmpAlerts/ModalAddDestination.vue
index 5eef381..38b1ced 100644
--- a/src/views/Settings/SnmpAlerts/ModalAddDestination.vue
+++ b/src/views/Settings/SnmpAlerts/ModalAddDestination.vue
@@ -1,8 +1,11 @@
<template>
- <b-modal id="add-destination" ref="modal" @ok="onOk" @hidden="resetForm">
- <template #modal-title>
- {{ $t('pageSnmpAlerts.modal.addSnmpDestinationTitle') }}
- </template>
+ <b-modal
+ id="add-destination"
+ ref="modal"
+ :title="$t('pageSnmpAlerts.modal.addSnmpDestinationTitle')"
+ @ok="onOk"
+ @hidden="resetForm"
+ >
<b-form id="form-destination">
<b-container>
<b-row>
@@ -64,7 +67,7 @@
</b-row>
</b-container>
</b-form>
- <template #modal-footer="{ cancel }">
+ <template #footer="{ cancel }">
<b-button variant="secondary" @click="cancel()">
{{ $t('global.action.cancel') }}
</b-button>
diff --git a/src/views/Settings/SnmpAlerts/SnmpAlerts.vue b/src/views/Settings/SnmpAlerts/SnmpAlerts.vue
index d18ea75..7dba367 100644
--- a/src/views/Settings/SnmpAlerts/SnmpAlerts.vue
+++ b/src/views/Settings/SnmpAlerts/SnmpAlerts.vue
@@ -2,7 +2,7 @@
<b-container fluid="xl">
<page-title :description="$t('pageSnmpAlerts.pageDescription')" />
<b-row>
- <b-col xl="9" class="text-right">
+ <b-col xl="9" class="text-end">
<b-button variant="primary" @click="initModalAddDestination">
<icon-add />
{{ $t('pageSnmpAlerts.addDestination') }}
@@ -13,7 +13,9 @@
<b-col xl="9">
<table-toolbar
ref="toolbar"
- :selected-items-count="selectedRows.length"
+ :selected-items-count="
+ Array.isArray(selectedRows) ? selectedRows.length : 0
+ "
:actions="tableToolbarActions"
@clear-selected="clearSelectedRows($refs.table)"
@batch-action="onBatchAction"
@@ -25,6 +27,7 @@
show-empty
no-select-on-click
hover
+ thead-class="table-light"
:fields="fields"
:items="tableItems"
:empty-text="$t('global.table.emptyMessage')"
@@ -36,9 +39,11 @@
v-model="tableHeaderCheckboxModel"
data-test-id="snmpAlerts-checkbox-selectAll"
:indeterminate="tableHeaderCheckboxIndeterminate"
- @change="onChangeHeaderCheckbox($refs.table)"
+ @change="onChangeHeaderCheckbox($refs.table, $event)"
>
- <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectAll') }}
+ </span>
</b-form-checkbox>
</template>
<template #cell(checkbox)="row">
@@ -47,7 +52,9 @@
:data-test-id="`snmpAlerts-checkbox-selectRow-${row.index}`"
@change="toggleSelectRow($refs.table, row.index)"
>
- <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+ <span class="visually-hidden-focusable">
+ {{ $t('global.table.selectItem') }}
+ </span>
</b-form-checkbox>
</template>
@@ -71,7 +78,7 @@
</b-col>
</b-row>
<!-- Modals -->
- <modal-add-destination @ok="onModalOk" />
+ <modal-add-destination v-model="showAddDestination" @ok="onModalOk" />
</b-container>
</template>
@@ -84,6 +91,7 @@
import TableRowAction from '@/components/Global/TableRowAction';
import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
import BVToastMixin from '@/components/Mixins/BVToastMixin';
+import { useModal } from 'bootstrap-vue-next';
import BVTableSelectableMixin, {
selectedRows,
@@ -108,9 +116,14 @@
this.hideLoader();
next();
},
+ setup() {
+ const bvModal = useModal();
+ return { bvModal };
+ },
data() {
return {
$t: useI18n().t,
+ showAddDestination: false,
fields: [
{
key: 'checkbox',
@@ -126,7 +139,7 @@
{
key: 'actions',
label: '',
- tdClass: 'text-right text-nowrap',
+ tdClass: 'text-end text-nowrap',
},
],
tableToolbarActions: [
@@ -201,28 +214,26 @@
.finally(() => this.endLoader());
},
initModalAddDestination() {
- this.$bvModal.show('add-destination');
+ this.showAddDestination = true;
},
initModalDeleteDestination(destination) {
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t('pageSnmpAlerts.modal.deleteConfirmMessage', {
- destination: destination.id,
- }),
- {
- title: i18n.global.t(
- 'pageSnmpAlerts.modal.deleteSnmpDestinationTitle',
- ),
- okTitle: i18n.global.t('pageSnmpAlerts.deleteDestination'),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfirmed) => {
- if (deleteConfirmed) {
- this.deleteDestination(destination);
- }
- });
+ this.confirmDialog(
+ i18n.global.t('pageSnmpAlerts.modal.deleteConfirmMessage', {
+ destination: destination.id,
+ }),
+ {
+ title: i18n.global.t(
+ 'pageSnmpAlerts.modal.deleteSnmpDestinationTitle',
+ ),
+ okTitle: i18n.global.t('pageSnmpAlerts.deleteDestination'),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ ).then((deleteConfirmed) => {
+ if (deleteConfirmed) {
+ this.deleteDestination(destination);
+ }
+ });
},
deleteDestination({ id }) {
this.startLoader();
@@ -234,44 +245,43 @@
},
onBatchAction(action) {
if (action === 'delete') {
- this.$bvModal
- .msgBoxConfirm(
- i18n.global.t(
- 'pageSnmpAlerts.modal.batchDeleteConfirmMessage',
- this.selectedRows.length,
+ const count = this.selectedRows.length;
+ this.confirmDialog(
+ i18n.global.t(
+ 'pageSnmpAlerts.modal.batchDeleteConfirmMessage',
+ count,
+ ),
+ {
+ title: i18n.global.t(
+ 'pageSnmpAlerts.modal.deleteSnmpDestinationTitle',
+ count,
),
- {
- title: i18n.global.t(
- 'pageSnmpAlerts.modal.deleteSnmpDestinationTitle',
- this.selectedRows.length,
- ),
- okTitle: i18n.global.t(
- 'pageSnmpAlerts.deleteDestination',
- this.selectedRows.length,
- ),
- cancelTitle: i18n.global.t('global.action.cancel'),
- autoFocusButton: 'ok',
- },
- )
- .then((deleteConfirmed) => {
- if (deleteConfirmed) {
- this.startLoader();
- this.$store
- .dispatch(
- 'snmpAlerts/deleteMultipleDestinations',
- this.selectedRows,
- )
- .then((messages) => {
- messages.forEach(({ type, message }) => {
- if (type === 'success') this.successToast(message);
- if (type === 'error') this.errorToast(message);
- });
- })
- .finally(() => this.endLoader());
- }
- });
+ okTitle: i18n.global.t('pageSnmpAlerts.deleteDestination', count),
+ cancelTitle: i18n.global.t('global.action.cancel'),
+ autoFocusButton: 'ok',
+ },
+ ).then((deleteConfirmed) => {
+ if (deleteConfirmed) {
+ this.startLoader();
+ this.$store
+ .dispatch(
+ 'snmpAlerts/deleteMultipleDestinations',
+ this.selectedRows,
+ )
+ .then((messages) => {
+ messages.forEach(({ type, message }) => {
+ if (type === 'success') this.successToast(message);
+ if (type === 'error') this.errorToast(message);
+ });
+ })
+ .finally(() => this.endLoader());
+ }
+ });
}
},
+ confirmDialog(message, options = {}) {
+ return this.$confirm({ message, ...options });
+ },
onTableRowAction(action, row) {
if (action === 'delete') {
this.initModalDeleteDestination(row);
diff --git a/vue.config.js b/vue.config.js
index 52820ab..3f3b34c 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -8,8 +8,7 @@
sass: {
additionalData: (() => {
const envName = process.env.VUE_APP_ENV_NAME;
- const hasCustomStyles =
- process.env.CUSTOM_STYLES === 'true' ? true : false;
+ const hasCustomStyles = process.env.CUSTOM_STYLES === 'true';
if (hasCustomStyles && envName !== undefined) {
return `
@import "@/assets/styles/bmc/helpers";
@@ -24,6 +23,24 @@
}
})(), // immediately invoked function expression (IIFE)
},
+ scss: {
+ additionalData: (() => {
+ const envName = process.env.VUE_APP_ENV_NAME;
+ const hasCustomStyles = process.env.CUSTOM_STYLES === 'true';
+ if (hasCustomStyles && envName !== undefined) {
+ return `
+ @import "@/assets/styles/bmc/helpers";
+ @import "@/env/assets/styles/_${envName}";
+ @import "@/assets/styles/bootstrap/_helpers";
+ `;
+ } else {
+ return `
+ @import "@/assets/styles/bmc/helpers";
+ @import "@/assets/styles/bootstrap/_helpers";
+ `;
+ }
+ })(),
+ },
},
},
devServer: {
@@ -43,14 +60,6 @@
},
productionSourceMap: false,
chainWebpack: (config) => {
- config.resolve.alias.set('vue', '@vue/compat');
- config.module
- .rule('vue')
- .use('vue-loader')
- .tap((options) => {
- options['compilerOptions'] = { compatConfig: { MODE: 2 } };
- return options;
- });
config.module
.rule('vue')
.use('vue-svg-inline-loader')
@@ -88,10 +97,9 @@
crypto_orig_createHash(algorithm == 'md4' ? 'sha256' : algorithm);
const envName = process.env.VUE_APP_ENV_NAME;
- const hasCustomStore = process.env.CUSTOM_STORE === 'true' ? true : false;
- const hasCustomRouter = process.env.CUSTOM_ROUTER === 'true' ? true : false;
- const hasCustomAppNav =
- process.env.CUSTOM_APP_NAV === 'true' ? true : false;
+ const hasCustomStore = process.env.CUSTOM_STORE === 'true';
+ const hasCustomRouter = process.env.CUSTOM_ROUTER === 'true';
+ const hasCustomAppNav = process.env.CUSTOM_APP_NAV === 'true';
if (envName !== undefined) {
if (hasCustomStore) {
@@ -126,6 +134,34 @@
};
config.optimization.runtimeChunk = false;
+
+ // Define Vue 3 compile-time feature flags
+ // These flags must be explicitly defined to avoid Vue warnings and optimize bundle size
+ config.plugins.push(
+ new webpack.DefinePlugin({
+ // Enable Options API support (required - this codebase uses Options API extensively)
+ // Setting to true includes Options API in the bundle (~3kb gzipped)
+ // Cannot be disabled until full migration to Composition API
+ __VUE_OPTIONS_API__: JSON.stringify(true),
+
+ // Disable Vue Devtools in production builds for security and performance
+ // Devtools automatically enabled in development mode regardless of this flag
+ __VUE_PROD_DEVTOOLS__: JSON.stringify(false),
+
+ // Disable detailed hydration mismatch warnings in production
+ // This is a SPA (not SSR), so hydration warnings don't apply
+ // Reduces bundle size and eliminates unnecessary runtime checks
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: JSON.stringify(false),
+
+ // Expose session storage toggle to client code
+ 'process.env.STORE_SESSION': JSON.stringify(
+ process.env.STORE_SESSION || '',
+ ),
+ 'process.env.VUE_APP_STORE_SESSION': JSON.stringify(
+ process.env.VUE_APP_STORE_SESSION || '',
+ ),
+ }),
+ );
},
pluginOptions: {
i18n: {