Merge branch 'develop' into feature/INSR-8325
This commit is contained in:
commit
6362735849
125 changed files with 5905 additions and 7314 deletions
59
.eslintrc.js
59
.eslintrc.js
|
|
@ -1,59 +0,0 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
jest: true
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
},
|
||||
extends: [
|
||||
'eslint-config-airbnb-base',
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:jsdoc/recommended'
|
||||
],
|
||||
rules: {
|
||||
'linebreak-style': 'off',
|
||||
'vue/component-definition-name-casing': ['warn', 'kebab-case'],
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/attribute-hyphenation': ['warn', 'never'],
|
||||
'vue/v-on-event-hyphenation': ['warn', 'never'],
|
||||
'object-curly-newline': ['error', { consistent: true }],
|
||||
'function-paren-newline': ['error', 'multiline'],
|
||||
'operator-linebreak': ['error', 'before', { overrides: { '=': 'after' } }],
|
||||
'implicit-arrow-linebreak': ['off'],
|
||||
'comma-dangle': ['error', 'never'],
|
||||
indent: ['error', 4, { SwitchCase: 1 }],
|
||||
'max-len': ['error', { code: 160 }],
|
||||
'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
|
||||
'vue/html-indent': 'off',
|
||||
'vue/html-closing-bracket-newline': ['error', {
|
||||
singleline: 'never',
|
||||
multiline: 'never'
|
||||
}],
|
||||
'jsdoc/check-tag-names': ['error', {
|
||||
definedTags: ['store', 'endpoint', 'category', 'subcategory', 'remarks']
|
||||
}],
|
||||
'jsdoc/require-jsdoc': 0,
|
||||
'vue/html-self-closing': ['error', {
|
||||
html: {
|
||||
void: 'any',
|
||||
normal: 'any',
|
||||
component: 'any'
|
||||
},
|
||||
svg: 'always',
|
||||
math: 'always'
|
||||
}],
|
||||
'import/extensions': ['error', 'always', { js: 'ignorePackages' }],
|
||||
'no-param-reassign': ['error', { props: true, ignorePropertyModificationsFor: ['item'] }],
|
||||
'no-restricted-syntax': ['off', 'ForOfStatement'],
|
||||
'no-return-await': 'off'
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
alias: {
|
||||
map: [['@', './src/']],
|
||||
extensions: ['.js', '.vue']
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
149
eslint.config.js
Normal file
149
eslint.config.js
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
import { globalIgnores } from 'eslint/config';
|
||||
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript';
|
||||
import pluginVue from 'eslint-plugin-vue';
|
||||
// import pluginVitest from '@vitest/eslint-plugin';
|
||||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
import jsdoc from 'eslint-plugin-jsdoc';
|
||||
|
||||
export default defineConfigWithVueTs(
|
||||
{
|
||||
name: 'app/files-to-lint',
|
||||
files: ['**/*.{vue,ts,js,mts,tsx}'],
|
||||
},
|
||||
|
||||
js.configs.recommended,
|
||||
jsdoc.configs['flat/recommended-mixed'],
|
||||
|
||||
// Add @stylistic configuration
|
||||
stylistic.configs.customize({
|
||||
indent: 4,
|
||||
quotes: 'single',
|
||||
semi: true,
|
||||
// Add any other stylistic preferences here
|
||||
}),
|
||||
|
||||
...pluginVue.configs['flat/essential'],
|
||||
vueTsConfigs.recommended,
|
||||
{
|
||||
name: 'app/global-rules',
|
||||
files: ['**/*.{js,ts,vue}'],
|
||||
rules: {
|
||||
'eqeqeq': ['error', 'smart'],
|
||||
'no-nested-ternary': 'error',
|
||||
'no-param-reassign': 'error',
|
||||
'jsdoc/require-jsdoc': 'off',
|
||||
'@stylistic/comma-dangle': 'off',
|
||||
'@stylistic/max-len': ['error', {
|
||||
code: 160,
|
||||
tabWidth: 4,
|
||||
ignoreUrls: true,
|
||||
ignoreStrings: true,
|
||||
ignoreTemplateLiterals: true
|
||||
}],
|
||||
|
||||
'vue/max-len': ['error', {
|
||||
code: 160,
|
||||
template: 160,
|
||||
tabWidth: 4,
|
||||
ignoreUrls: true,
|
||||
ignoreStrings: true,
|
||||
ignoreTemplateLiterals: true,
|
||||
ignoreHTMLTextContents: false
|
||||
}],
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'app/custom-vue-rules',
|
||||
files: ['**/*.vue'],
|
||||
rules: {
|
||||
'vue/block-lang': ['error', {
|
||||
script: {
|
||||
lang: ['js', 'ts'],
|
||||
allowNoLang: true // Still allow <script> without a lang tag
|
||||
},
|
||||
style: {
|
||||
lang: 'scss',
|
||||
allowNoLang: false // Disallow <style> without lang="scss"
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'app/custom-js-rules',
|
||||
files: ['**/*.{js,ts}'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
...globals.vitest,
|
||||
...globals.vue,
|
||||
...globals.jest
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Additional test globals (object) — applies only to test files
|
||||
// {
|
||||
// files: ['**/*.spec.*', '**/*.test.*', '**/unit-test-helper.js'],
|
||||
// ...pluginVitest.configs.recommended,
|
||||
// },
|
||||
|
||||
{
|
||||
name: 'app/temp-rules',
|
||||
rules: {
|
||||
// Temp rules until we make a pass at fixing all of them.
|
||||
'@stylistic/arrow-parens': 'off',
|
||||
'@stylistic/brace-style': 'off',
|
||||
'@stylistic/eol-last': 'off',
|
||||
'@stylistic/indent': 'off',
|
||||
'@stylistic/indent-binary-ops': 'off',
|
||||
'@stylistic/max-len': 'off',
|
||||
'@stylistic/max-statements-per-line': 'off',
|
||||
'@stylistic/multiline-ternary': 'off',
|
||||
'@stylistic/no-trailing-spaces': 'off',
|
||||
'@stylistic/object-curly-spacing': 'off',
|
||||
'@stylistic/operator-linebreak': 'off',
|
||||
'@stylistic/quote-props': 'off',
|
||||
'@stylistic/quotes': 'off',
|
||||
'@stylistic/semi': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'jsdoc/reject-any-type': 'off',
|
||||
'jsdoc/require-param-description': 'off',
|
||||
'jsdoc/require-param-type': 'off',
|
||||
'jsdoc/require-returns-description': 'off',
|
||||
'jsdoc/require-returns': 'off',
|
||||
'jsdoc/require-returns-type': 'off',
|
||||
'jsdoc/ts-no-empty-object-type': 'off',
|
||||
'no-case-declarations': 'off',
|
||||
'no-constant-binary-expression': 'off',
|
||||
'no-dupe-keys': 'off',
|
||||
'no-import-assign': 'off',
|
||||
'no-shadow': 'off',
|
||||
'vitest/no-conditional-expect': 'off',
|
||||
'vitest/no-identical-title': 'off',
|
||||
'vitest/valid-expect': 'off',
|
||||
'vitest/valid-expect-in-promise': 'off',
|
||||
'vitest/valid-title': 'off',
|
||||
'vue/max-len': 'off',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/no-reserved-component-names': 'off',
|
||||
'vue/no-side-effects-in-computed-properties': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'vitest/no-commented-out-tests': 'off',
|
||||
'vitest/no-focused-tests': 'off',
|
||||
'no-undef': 'off',
|
||||
'vue/require-toggle-inside-transition': 'off',
|
||||
'vue/no-dupe-keys': 'off',
|
||||
'@typescript-eslint/no-this-alias': 'off',
|
||||
'vitest/expect-expect': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
}
|
||||
},
|
||||
|
||||
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**', 'playwright-tests', '**/*.snap']),
|
||||
);
|
||||
8182
package-lock.json
generated
8182
package-lock.json
generated
File diff suppressed because it is too large
Load diff
17
package.json
17
package.json
|
|
@ -14,7 +14,10 @@
|
|||
"test:unit": "vue-cli-service test:unit --coverage --ci --colors",
|
||||
"test:unit:coverage": "vue-cli-service test:unit --coverage --ci --colors",
|
||||
"test:unit:lite": "vue-cli-service test:unit --ci",
|
||||
"test:playwright": "playwright test --config=playwright-tests/playwright.config.ts"
|
||||
"test:playwright": "playwright test --config=playwright-tests/playwright.config.ts",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"lint:inspect": "eslint --inspect-config"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.13.5",
|
||||
|
|
@ -31,11 +34,13 @@
|
|||
"vue-router": "4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@faker-js/faker": "^9.0.3",
|
||||
"@pinia/testing": "0.1.2",
|
||||
"@playwright/test": "^1.56.1",
|
||||
"@rushstack/eslint-patch": "^1.3.2",
|
||||
"@saucelabs/playwright-reporter": "^1.5.0",
|
||||
"@stylistic/eslint-plugin": "^5.6.1",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/user-event": "14.4.3",
|
||||
"@testing-library/vue": "6.6.1",
|
||||
|
|
@ -48,6 +53,8 @@
|
|||
"@vue/cli-plugin-router": "~5.0.0",
|
||||
"@vue/cli-plugin-unit-jest": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"@vue/eslint-config-prettier": "^10.2.0",
|
||||
"@vue/eslint-config-typescript": "^14.6.0",
|
||||
"@vue/test-utils": "^2.4.1",
|
||||
"@vue/vue3-jest": "^27.0.0-alpha.1",
|
||||
"axe-core": "^4.10.2",
|
||||
|
|
@ -57,8 +64,11 @@
|
|||
"concurrently": "^9.1.2",
|
||||
"dotenv-safe": "^9.1.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-vue": "^9.15.1",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jsdoc": "^61.5.0",
|
||||
"eslint-plugin-vue": "^10.6.2",
|
||||
"form-data": "^4.0.4",
|
||||
"globals": "^17.0.0",
|
||||
"jest": "^27.0.5",
|
||||
"jest-junit": "^13.0.0",
|
||||
"jest-serializer-vue": "^3.1.0",
|
||||
|
|
@ -66,13 +76,14 @@
|
|||
"jsdom": "^22.1.0",
|
||||
"luxon": "^3.5.0",
|
||||
"ortoni-report": "^2.0.8",
|
||||
"prettier": "^3.7.4",
|
||||
"sass": "^1.77.8",
|
||||
"sass-loader": "^12.0.0",
|
||||
"saucectl": "^0.188.0",
|
||||
"typescript-eslint": "^8.11.0",
|
||||
"vite": "^6.4.1",
|
||||
"vitest": "^3.2.4",
|
||||
"volar-service-vetur": "latest",
|
||||
"vue-eslint-parser": "^10.2.0",
|
||||
"wait-on": "^8.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default {
|
|||
watch: {
|
||||
shouldShowLoader(newVal) {
|
||||
// prevent keyboard input when loader is shown, re-enable when hidden
|
||||
if(newVal) {
|
||||
if (newVal) {
|
||||
document.onkeydown = () => false;
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
BIN
src/assets/img/icons/select-disabled.png
Normal file
BIN
src/assets/img/icons/select-disabled.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/img/tints/GlassTint.png
Normal file
BIN
src/assets/img/tints/GlassTint.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
|
|
@ -93,6 +93,10 @@ const endpoints = Object.freeze({
|
|||
) => `${LOCATION_BASE_URL}/providers/${zipCode}/${damageType}/${radius}/${parentAccountNumber}/${safeliteOnly}/${carId}/${recalPartNumber != null ? `${recalPartNumber}/` : ''}${isBigTruck}`,
|
||||
method: 'GET'
|
||||
},
|
||||
GetTpaAndSafeliteProviders: {
|
||||
url: `${LOCATION_BASE_URL}/tpa-and-safelite-providers`,
|
||||
method: 'GET'
|
||||
},
|
||||
GetCapabilityQuestions: {
|
||||
url: `${PARTS_BASE_URL}/capability-questions`,
|
||||
method: 'GET'
|
||||
|
|
@ -118,7 +122,6 @@ const endpoints = Object.freeze({
|
|||
zipCode,
|
||||
applicationName,
|
||||
referralSequenceNumber
|
||||
// eslint-disable-next-line max-len
|
||||
) => `${PARTS_BASE_URL}/recal-parts/${carId}/${partNumber}/${recalibrationType}/${parentAccountNumber}/${zipCode}/${applicationName}/${referralSequenceNumber}`,
|
||||
method: 'GET'
|
||||
},
|
||||
|
|
|
|||
|
|
@ -31,9 +31,7 @@ const errorMessages = Object.freeze({
|
|||
INVALID_ZIP: 'The ZIP you entered was invalid. Please enter a valid ZIP.',
|
||||
ZIP_CODE_NOT_SERVICED_FOR_VEHICLE: 'We do not currently offer glass service for your vehicle in this ZIP code. Please try another ZIP code.',
|
||||
VIN_REQUIRED: 'Please enter your VIN',
|
||||
VIN_FORMAT:
|
||||
// eslint-disable-next-line max-len
|
||||
'Invalid VIN. Please make sure that you entered the correct 17-digit, alpha-numeric number. VINs do not contain the letters I, O, or Q',
|
||||
VIN_FORMAT: 'Invalid VIN. Please make sure that you entered the correct 17-digit, alpha-numeric number. VINs do not contain the letters I, O, or Q',
|
||||
OPTION_REQUIRED: 'Please select an option',
|
||||
VEHICLE_REQUIRED: 'Please select a vehicle',
|
||||
POLICY_NUMBER_REQUIRED: 'Policy number is required.',
|
||||
|
|
@ -67,7 +65,12 @@ const errorMessages = Object.freeze({
|
|||
DATE_REQUIRED: 'Please select a date',
|
||||
TIME_REQUIRED: 'Please select an appointment time.',
|
||||
PRIMARY_PHONE_REQUIRED: 'Primary phone number is required.',
|
||||
BAILOUT_EMAIL_ADDRESS_REQUIRED: 'Email address is required.'
|
||||
BAILOUT_EMAIL_ADDRESS_REQUIRED: 'Email address is required.',
|
||||
TPA_SEARCH_ZIP_FORMAT: 'Please enter a valid Zip code.',
|
||||
TPA_SEARCH_SHOP_FORMAT: 'Please enter a valid shop name.',
|
||||
TPA_SEARCH_REQUIRED: 'Please enter a shop name or ZIP code.',
|
||||
SERVICE_ADDRESS_REQUIRED: 'Service street address is required.',
|
||||
SERVICE_CITY_REQUIRED: 'Service city is required.'
|
||||
});
|
||||
|
||||
export default errorMessages;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ const globalRules = Object.freeze({
|
|||
ZIP_CODE_SEARCH_FORMAT: 'zip-code-search-format',
|
||||
OPTION_REQUIRED: 'option-required',
|
||||
PRIMARY_PHONE_REQUIRED: 'primary-phone-required',
|
||||
BAILOUT_EMAIL_ADDRESS_REQUIRED: 'bailout-email-address-required'
|
||||
BAILOUT_EMAIL_ADDRESS_REQUIRED: 'bailout-email-address-required',
|
||||
TPA_SEARCH_FORMAT: 'tpa-search-format'
|
||||
});
|
||||
|
||||
export default globalRules;
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
// TintMap with array keys by location, lowercase to avoid as much string mismatching as possible.
|
||||
// Src assumes you have a @/assets/img/tints/, making the final value @/assets/img/tints/{Src} in the markup.
|
||||
// See vehicle-parts for implementation example.
|
||||
const tintMap = Object.freeze({
|
||||
other: [
|
||||
// Blue Shade
|
||||
{ name: 'blue tint, blue shade', src: 'Glass-BlueShade-BlueTint.svg' },
|
||||
{ name: 'brown tint, blue shade', src: 'Glass-BlueShade-BrownTint.svg' },
|
||||
{ name: 'gray tint, blue shade', src: 'Glass-BlueShade-GrayTint.svg' },
|
||||
{ name: 'green tint, blue shade', src: 'Glass-BlueShade-GreenTint.svg' },
|
||||
{ name: 'clear, blue shade', src: 'Glass-BlueShade-NoTint.svg' },
|
||||
|
||||
// Brown Shade
|
||||
{ name: 'brown tint, brown shade', src: 'Glass-BrownShade-BrownTint.svg' },
|
||||
|
||||
// Gray Shade
|
||||
{ name: 'blue tint, gray shade', src: 'Glass-GrayShade-BlueTint.svg' },
|
||||
{ name: 'brown tint, gray shade', src: 'Glass-GrayShade-BrownTint.svg' },
|
||||
{ name: 'gray tint, gray shade', src: 'Glass-GrayShade-GrayTint.svg' },
|
||||
{ name: 'green tint, gray shade', src: 'Glass-GrayShade-GreenTint.svg' },
|
||||
|
||||
// Green Shade
|
||||
{ name: 'blue tint, green shade', src: 'Glass-GreenShade-BlueTint.svg' },
|
||||
{ name: 'brown tint, green shade', src: 'Glass-GreenShade-BrownTint.svg' },
|
||||
{ name: 'green tint, green shade', src: 'Glass-GreenShade-GreenTint.svg' },
|
||||
|
||||
// Tints Only
|
||||
{ name: 'blue tint privacy', src: 'Glass-NoShade-BlueTint.svg' },
|
||||
{ name: 'bronze tint', src: 'Glass-NoShade-BrownTint.svg' },
|
||||
{ name: 'dark brown tint', src: 'Glass-NoShade-DarkBrownTint.svg' },
|
||||
{ name: 'privacy, black frame', src: 'Glass-NoShade-Privacy.svg' },
|
||||
{ name: 'gray tint', src: 'Glass-NoShade-GrayTint.svg' },
|
||||
{ name: 'green tint', src: 'Glass-NoShade-GreenTint.svg' },
|
||||
{ name: 'gray tint privacy', src: 'Glass-NoShade-GrayTint.svg' },
|
||||
{ name: 'blue tint', src: 'Glass-NoShade-BlueTint.svg' },
|
||||
{ name: 'green tint privacy', src: 'Glass-NoShade-GreenTint.svg' },
|
||||
|
||||
// No shade or tint
|
||||
{ name: 'clear', src: 'Glass-NoShade-NoTint.svg' }
|
||||
],
|
||||
|
||||
windshield: [
|
||||
// Blue Shade
|
||||
{ name: 'blue tint, blue shade', src: 'Windshield-BlueShade-BlueTint.svg' },
|
||||
{ name: 'bronze tint, blue shade', src: 'Windshield-BlueShade-BrownTint.svg' },
|
||||
{ name: 'gray tint, blue shade', src: 'Windshield-BlueShade-GrayTint.svg' },
|
||||
{ name: 'green tint, blue shade', src: 'Windshield-BlueShade-GreenTint.svg' },
|
||||
{ name: 'clear, blue shade', src: 'Windshield-BlueShade-NoTint.svg' },
|
||||
|
||||
// Brown Shade
|
||||
{ name: 'bronze tint, bronze shade', src: 'Windshield-BrownShade-BrownTint.svg' },
|
||||
|
||||
// Gray Shade
|
||||
{ name: 'blue tint, gray shade', src: 'Windshield-GrayShade-BlueTint.svg' },
|
||||
{ name: 'brown tint, gray shade', src: 'Windshield-GrayShade-BrownTint.svg' },
|
||||
{ name: 'gray tint, gray shade', src: 'Windshield-GrayShade-GrayTint.svg' },
|
||||
{ name: 'green tint, gray shade', src: 'Windshield-GrayShade-GreenTint.svg' },
|
||||
|
||||
// Green Shade
|
||||
{ name: 'blue tint, green shade', src: 'Windshield-GreenShade-BlueTint.svg' },
|
||||
{ name: 'brown tint, green shade', src: 'Windshield-GreenShade-BrownTint.svg' },
|
||||
{ name: 'green tint, green shade', src: 'Windshield-GreenShade-GreenTint.svg' },
|
||||
|
||||
// Tints Only
|
||||
{ name: 'blue tint', src: 'Windshield-NoShade-BlueTint.svg' },
|
||||
{ name: 'bronze tint', src: 'Windshield-NoShade-BrownTint.svg' },
|
||||
{ name: 'dark brown tint', src: 'Windshield-NoShade-DarkBrownTint.svg' },
|
||||
{ name: 'privacy, black frame', src: 'Windshield-NoShade-Privacy.svg' },
|
||||
{ name: 'gray tint', src: 'Windshield-NoShade-GrayTint.svg' },
|
||||
{ name: 'green tint', src: 'Windshield-NoShade-GreenTint.svg' },
|
||||
{ name: 'gray tint privacy', src: 'Windshield-NoShade-GrayTint.svg' },
|
||||
|
||||
// No shade or tint
|
||||
{ name: 'clear', src: 'Windshield-NoShade-NoTint.svg' }
|
||||
]
|
||||
});
|
||||
|
||||
// Gets the tint image source string given the glass location, and the tint description (like 'Green Tint')
|
||||
// Use lowered strings here to try to avoid mismatch. Returns an empty string if array key doesn't exist.
|
||||
// Returns undefined if no items are found.
|
||||
const getTintImage = (glassLocation, colorString) => {
|
||||
// Windshield glass has special images, all other glass uses the same though.
|
||||
const glassLoc = (glassLocation.toLowerCase() !== 'windshield') ? 'other' : 'windshield';
|
||||
if (tintMap[glassLoc] === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return tintMap[glassLoc].find((item) => item.name.toLowerCase() === colorString.toLowerCase());
|
||||
};
|
||||
|
||||
export default getTintImage;
|
||||
|
|
@ -119,7 +119,6 @@ export default {
|
|||
this.handleClick(e);
|
||||
break;
|
||||
case this.eventTypes.CHANGE:
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
this.selectingInitiatesLoad
|
||||
? this.handleSelectionChange(e)
|
||||
: this.handleClick(e);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable max-len */
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ export default {
|
|||
isOverflowScrollable: Boolean,
|
||||
isWide: Boolean,
|
||||
isCashOrInsurance: Boolean,
|
||||
isHorizontalLayout: Boolean,
|
||||
modelValue: [Array, Number, String],
|
||||
value: [Number, String],
|
||||
validationRules: String,
|
||||
|
|
@ -211,7 +212,10 @@ export default {
|
|||
classes = 'ui-radio d-flex';
|
||||
break;
|
||||
case 'servicePackageRadio':
|
||||
classes = 'package-main';
|
||||
classes = 'package-main d-flex flex-column';
|
||||
if (this.isHorizontalLayout) {
|
||||
classes += ' flex-md-row gap-3';
|
||||
}
|
||||
break;
|
||||
case 'providerPrefRadio':
|
||||
classes = 'option-main';
|
||||
|
|
@ -231,6 +235,9 @@ export default {
|
|||
break;
|
||||
case 'servicePackageRadio':
|
||||
classes = 'package-wrapper';
|
||||
if (this.isHorizontalLayout) {
|
||||
classes += ' flex-grow-0 flex-shrink-0';
|
||||
}
|
||||
break;
|
||||
case 'providerPrefRadio':
|
||||
classes = 'option-wrapper';
|
||||
|
|
@ -325,6 +332,12 @@ export default {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.button-question {
|
||||
:deep(.package-wrapper) {
|
||||
@media (min-width: 768px) {
|
||||
flex: 0 0 calc(33.333% - 0.67rem);
|
||||
max-width: calc(33.333% - 0.67rem);
|
||||
}
|
||||
}
|
||||
color: $black;
|
||||
|
||||
.radio-button-container {
|
||||
|
|
@ -345,6 +358,11 @@ export default {
|
|||
&.button-question-text-left {
|
||||
text-align: left;
|
||||
}
|
||||
&.service-package-question-text {
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,9 +99,9 @@
|
|||
</div>
|
||||
<template
|
||||
v-for="timeSlot in selectableTimeSlotsData"
|
||||
:key="timeSlot.startTime">
|
||||
:key="`${timeSlot.startTime}-${timeSlot.isDropoff}`">
|
||||
<input
|
||||
:id="`timeslot${timeSlot.startTime}`"
|
||||
:id="`timeslot${timeSlot.startTime}-${timeSlot.isDropoff}`"
|
||||
v-model="selectedTime"
|
||||
type="radio"
|
||||
name="time-slot"
|
||||
|
|
@ -114,9 +114,14 @@
|
|||
class="time-slot-button"
|
||||
:class="[checkIsSelectedTimeSlot(timeSlot) ? 'selected-time-slot' : ''
|
||||
, {'has-date-error': showTimeSlotError}]"
|
||||
@click="selectTimeSlotForDay(timeSlot)">
|
||||
{{ displayTimeSlotTime(timeSlot) }}
|
||||
@click="selectTimeSlotForDay(timeSlot)"
|
||||
v-html="displayTimeSlotTime(timeSlot)">
|
||||
</label>
|
||||
<div
|
||||
v-if="timeSlot.isDropoff"
|
||||
class="time-slot-drop-off-information"
|
||||
v-html="displayDropOffInformation(timeSlot)">
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="row form-test-error">
|
||||
|
|
@ -256,6 +261,10 @@ export default {
|
|||
appointmentEstimate() {
|
||||
return getDisplayTextForDurationLength(this.appointmentDurationMinutesMinimum, this.appointmentDurationMinutesMaximum);
|
||||
},
|
||||
calendarViewDirection() {
|
||||
if (this.selectableDatesSetting === 'custom') return 'future';
|
||||
return 'past';
|
||||
},
|
||||
daysToAdd() {
|
||||
return this.isMobileView ? this.daysInViewMobile : this.daysInViewStandard;
|
||||
},
|
||||
|
|
@ -284,10 +293,6 @@ export default {
|
|||
this.todayOverrideDateString
|
||||
|| convertDateToDateString(new Date())
|
||||
);
|
||||
},
|
||||
calendarViewDirection() {
|
||||
if (this.selectableDatesSetting === 'custom') return 'future';
|
||||
return 'past';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -301,16 +306,16 @@ export default {
|
|||
this.findFirstAvailableDateInView();
|
||||
}
|
||||
},
|
||||
selectedDate(newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
this.$emit('dateSelected', newValue);
|
||||
}
|
||||
},
|
||||
selectedTimeSlot(newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
const testObj = this.getSelectedTimeSlotInfoObject(newValue);
|
||||
this.$emit('timeSlotSelected', testObj);
|
||||
}
|
||||
},
|
||||
selectedDate(newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
this.$emit('dateSelected', newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -340,50 +345,44 @@ export default {
|
|||
addPremiumFlagToInput(routeCode) {
|
||||
return (`${routeCode}${PREMIUM_TIME_SLOT_ID_FLAG}`);
|
||||
},
|
||||
removePremiumFlagFromInput(routeCode) {
|
||||
return routeCode.replace(PREMIUM_TIME_SLOT_ID_FLAG, '');
|
||||
checkIsSelectedDay(index, timeOfDayGrouping) {
|
||||
const dateToShowString = this.getDateToShowString(index);
|
||||
return this.selectedDate === dateToShowString && this.selectedTimeOfDayGrouping === timeOfDayGrouping;
|
||||
},
|
||||
getSelectedTimeSlotInfoObject(timeSlot) {
|
||||
const routeCode = timeSlot?.id;
|
||||
let routeCodeToUse = routeCode;
|
||||
const routeCodeIncludesPremium = routeCode?.includes(PREMIUM_TIME_SLOT_ID_FLAG);
|
||||
if (routeCodeIncludesPremium) {
|
||||
routeCodeToUse = this.removePremiumFlagFromInput(routeCode);
|
||||
checkIsSelectedTimeSlot(timeSlot) {
|
||||
if (!this.selectedTimeSlot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const fullTimeSlot = this.selectableTimeSlotsData?.find((ts) => ts.id === routeCodeToUse);
|
||||
|
||||
if (fullTimeSlot) {
|
||||
return {
|
||||
timeSlot: {
|
||||
date: this.selectedDate,
|
||||
routeCode: fullTimeSlot.id,
|
||||
startTime: fullTimeSlot.startTime,
|
||||
endTime: fullTimeSlot.endTime,
|
||||
jobMaxMinutes: this.appointmentDurationMinutesMaximum.toString(),
|
||||
jobMinMinutes: this.appointmentDurationMinutesMinimum.toString()
|
||||
},
|
||||
isPremiumAppointment: !!routeCodeIncludesPremium
|
||||
};
|
||||
return (this.selectedTimeSlot.isDropoff === timeSlot.isDropoff
|
||||
&& this.selectedTimeSlot.startTime === timeSlot.startTime
|
||||
&& this.selectedTimeSlot.timeOfDay === timeSlot.timeOfDay);
|
||||
},
|
||||
displayDropOffInformation(timeSlot) {
|
||||
if (timeSlot.isDropoff) {
|
||||
if (timeSlot.timeOfDay === 'morning') {
|
||||
return this.getCmsContent(
|
||||
'DropOffTimeSlotModal',
|
||||
'BodyText'
|
||||
);
|
||||
}
|
||||
return this.getCmsContent(
|
||||
'OvernightDropOffTimeSlotModal',
|
||||
'BodyText'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
timeSlot: {
|
||||
date: null,
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
routeCode: null,
|
||||
jobMaxMinutes: null,
|
||||
jobMinMinutes: null
|
||||
},
|
||||
isPremiumAppointment: null
|
||||
};
|
||||
return '';
|
||||
},
|
||||
displayTimeSlotTime(timeSlot) {
|
||||
const appointmentType = this.activeAppointmentType || AppointmentTypeStrings.IN_SHOP;
|
||||
if (appointmentType === AppointmentTypeStrings.MOBILE || appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) {
|
||||
return `${militaryToTwelveHourTime(timeSlot.startTime)} - ${militaryToTwelveHourTime(timeSlot.endTime)}`;
|
||||
}
|
||||
if (timeSlot.isDropoff) {
|
||||
if (timeSlot.timeOfDay === 'morning') {
|
||||
return '<span class="dropoff-label">Drop & Go<sup>™</sup></span>';
|
||||
}
|
||||
return '<span class="dropoff-label">Overnight drop off</span>';
|
||||
}
|
||||
return militaryToTwelveHourTime(timeSlot.startTime);
|
||||
},
|
||||
findFirstAvailableDateInView() {
|
||||
|
|
@ -436,8 +435,7 @@ export default {
|
|||
if (!initialTimeSlot || !initialTimeSlot.startTime) {
|
||||
return;
|
||||
}
|
||||
const initialStartTime = initialTimeSlot.startTime;
|
||||
const foundTimeSlot = timeSlots.find((ts) => ts.startTime === initialStartTime);
|
||||
const foundTimeSlot = timeSlots.find((ts) => ts.startTime === initialTimeSlot.startTime && ts.endTime === initialTimeSlot.endTime);
|
||||
if (foundTimeSlot) {
|
||||
this.selectTimeSlotForDay(foundTimeSlot);
|
||||
}
|
||||
|
|
@ -452,6 +450,44 @@ export default {
|
|||
const dateToShow = this.getDateToShow(index);
|
||||
return convertDateToDateString(dateToShow);
|
||||
},
|
||||
getSelectedTimeSlotInfoObject(timeSlot) {
|
||||
const routeCode = timeSlot?.id;
|
||||
let routeCodeToUse = routeCode;
|
||||
const routeCodeIncludesPremium = routeCode?.includes(PREMIUM_TIME_SLOT_ID_FLAG);
|
||||
if (routeCodeIncludesPremium) {
|
||||
routeCodeToUse = this.removePremiumFlagFromInput(routeCode);
|
||||
}
|
||||
|
||||
const fullTimeSlot = this.selectableTimeSlotsData?.find((ts) => ts.id === routeCodeToUse);
|
||||
|
||||
if (fullTimeSlot) {
|
||||
return {
|
||||
timeSlot: {
|
||||
date: this.selectedDate,
|
||||
routeCode: fullTimeSlot.id,
|
||||
startTime: fullTimeSlot.startTime,
|
||||
endTime: fullTimeSlot.endTime,
|
||||
isDropoff: fullTimeSlot.isDropoff,
|
||||
jobMaxMinutes: this.appointmentDurationMinutesMaximum.toString(),
|
||||
jobMinMinutes: this.appointmentDurationMinutesMinimum.toString()
|
||||
},
|
||||
isPremiumAppointment: !!routeCodeIncludesPremium
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
timeSlot: {
|
||||
date: null,
|
||||
routeCode: null,
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
isDropoff: null,
|
||||
jobMaxMinutes: null,
|
||||
jobMinMinutes: null
|
||||
},
|
||||
isPremiumAppointment: null
|
||||
};
|
||||
},
|
||||
async gotoNextPage() {
|
||||
const maxPageIndex = Math.floor((this.daysLoaded - 1) / this.daysToAdd);
|
||||
if (this.activePageIndex >= maxPageIndex) {
|
||||
|
|
@ -479,15 +515,15 @@ export default {
|
|||
this.selectedTimeSlot = null;
|
||||
this.findFirstAvailableDateInView();
|
||||
},
|
||||
checkIsSelectedDay(index, timeOfDayGrouping) {
|
||||
const dateToShowString = this.getDateToShowString(index);
|
||||
return this.selectedDate === dateToShowString && this.selectedTimeOfDayGrouping === timeOfDayGrouping;
|
||||
mapTimeSlot(timeSlot, timeOfDay) {
|
||||
return {
|
||||
...timeSlot,
|
||||
isDropoff: timeSlot.id.endsWith('DROP OFF'),
|
||||
timeOfDay
|
||||
};
|
||||
},
|
||||
checkIsSelectedTimeSlot(timeSlot) {
|
||||
if (!this.selectedTimeSlot) {
|
||||
return false;
|
||||
}
|
||||
return this.selectedTimeSlot.startTime === timeSlot.startTime;
|
||||
removePremiumFlagFromInput(routeCode) {
|
||||
return routeCode.replace(PREMIUM_TIME_SLOT_ID_FLAG, '');
|
||||
},
|
||||
selectTimeSlotForDay(timeSlot) {
|
||||
if (timeSlot) {
|
||||
|
|
@ -567,13 +603,28 @@ export default {
|
|||
morningTimeSlots: selectableDate.timeSlots.filter((timeSlot) => {
|
||||
const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10);
|
||||
return timeHour < 12;
|
||||
}),
|
||||
}).map((timeSlot) => this.mapTimeSlot(timeSlot, 'morning')),
|
||||
afternoonTimeSlots: selectableDate.timeSlots.filter((timeSlot) => {
|
||||
const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10);
|
||||
return timeHour >= 12;
|
||||
}),
|
||||
}).map((timeSlot) => this.mapTimeSlot(timeSlot, 'afternoon')),
|
||||
isSelected: false
|
||||
};
|
||||
|
||||
if (dateObjectToPush.morningTimeSlots.length > 0) {
|
||||
const findIndex = dateObjectToPush.morningTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF'));
|
||||
if (findIndex !== -1) {
|
||||
const dropOffSlot = dateObjectToPush.morningTimeSlots.splice(findIndex, 1)[0];
|
||||
dateObjectToPush.morningTimeSlots.unshift(dropOffSlot);
|
||||
}
|
||||
}
|
||||
if (dateObjectToPush.afternoonTimeSlots.length > 0) {
|
||||
const findIndex = dateObjectToPush.afternoonTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF'));
|
||||
if (findIndex !== -1) {
|
||||
const dropOffSlot = dateObjectToPush.afternoonTimeSlots.splice(findIndex, 1)[0];
|
||||
dateObjectToPush.afternoonTimeSlots.push(dropOffSlot);
|
||||
}
|
||||
}
|
||||
this.selectableDatesData.push(dateObjectToPush);
|
||||
});
|
||||
|
||||
|
|
@ -593,7 +644,6 @@ export default {
|
|||
this.findFirstAvailableDateInView();
|
||||
let attempts = 0;
|
||||
while (!this.selectedDate && attempts < 5) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await this.gotoNextPage();
|
||||
attempts += 1;
|
||||
}
|
||||
|
|
@ -636,14 +686,28 @@ export default {
|
|||
morningTimeSlots: selectableDate.timeSlots.filter((timeSlot) => {
|
||||
const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10);
|
||||
return timeHour < 12;
|
||||
}),
|
||||
}).map((timeSlot) => this.mapTimeSlot(timeSlot, 'morning')),
|
||||
afternoonTimeSlots: selectableDate.timeSlots.filter((timeSlot) => {
|
||||
const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10);
|
||||
return timeHour >= 12;
|
||||
}),
|
||||
}).map((timeSlot) => this.mapTimeSlot(timeSlot, 'afternoon')),
|
||||
isSelected: false
|
||||
};
|
||||
|
||||
if (dateObjectToPush.morningTimeSlots.length > 0) {
|
||||
const findIndex = dateObjectToPush.morningTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF'));
|
||||
if (findIndex !== -1) {
|
||||
const dropOffSlot = dateObjectToPush.morningTimeSlots.splice(findIndex, 1)[0];
|
||||
dateObjectToPush.morningTimeSlots.unshift(dropOffSlot);
|
||||
}
|
||||
}
|
||||
if (dateObjectToPush.afternoonTimeSlots.length > 0) {
|
||||
const findIndex = dateObjectToPush.afternoonTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF'));
|
||||
if (findIndex !== -1) {
|
||||
const dropOffSlot = dateObjectToPush.afternoonTimeSlots.splice(findIndex, 1)[0];
|
||||
dateObjectToPush.afternoonTimeSlots.push(dropOffSlot);
|
||||
}
|
||||
}
|
||||
this.selectableDatesData.push(dateObjectToPush);
|
||||
}
|
||||
});
|
||||
|
|
@ -833,16 +897,55 @@ export default {
|
|||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
:deep(.dropoff-label) {
|
||||
sup {
|
||||
top: -0.375rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
& + .time-slot-drop-off-information {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.75s ease-in-out;
|
||||
font-weight: 400;
|
||||
text-align: left;
|
||||
color: $darker-gray;
|
||||
|
||||
:deep(ul) {
|
||||
margin: 0;
|
||||
margin-bottom: 0.375rem;
|
||||
|
||||
> li {
|
||||
padding-left: 0.625rem;
|
||||
margin-top: 0.625rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.selected-time-slot {
|
||||
background-color: $background-color-selected;
|
||||
border: solid 1px #0070d1;
|
||||
color:#000;
|
||||
font-weight: 500;
|
||||
|
||||
:deep(.dropoff-label) {
|
||||
sup {
|
||||
top: .25rem;
|
||||
font-size: 1.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
& + .time-slot-drop-off-information {
|
||||
max-height: 12rem;
|
||||
transition: max-height 0.75s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&.has-date-error {
|
||||
border: 1px solid #d93025;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,8 +156,8 @@ export default {
|
|||
&:disabled,
|
||||
&.disabled {
|
||||
background-color: $gray-100;
|
||||
color: $gray-500;
|
||||
filter: grayscale(100%);
|
||||
background-image: url(~@/assets/img/icons/select-disabled.png);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
&.dropdown-compact {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ import { useForm } from 'vee-validate';
|
|||
import { modalPositions } from '@/constants/component-variants';
|
||||
|
||||
export default {
|
||||
// eslint-disable-next-line vue/multi-word-component-names
|
||||
name: 'modal',
|
||||
components: {
|
||||
ButtonMain
|
||||
|
|
@ -103,7 +102,6 @@ export default {
|
|||
// TODO: Correct Duplicate key 'modalId' issue.
|
||||
// Probably just rename the prop. -br
|
||||
return {
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
modalId,
|
||||
meta,
|
||||
validate,
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ export default {
|
|||
initialValue
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
const { errorMessage, handleBlur, handleChange, meta, validate, errors } =
|
||||
useField(props.inputId, props.validationRules, fieldOptions);
|
||||
|
||||
|
|
@ -310,22 +309,31 @@ input::-webkit-date-and-time-value {
|
|||
.input-wrapper {
|
||||
position: relative;
|
||||
&.has-search-icon {
|
||||
display:flex;
|
||||
.form-control {
|
||||
&:focus, &:focus-within {
|
||||
border: 1px solid #0070d1;
|
||||
}
|
||||
max-height: 3rem;
|
||||
}
|
||||
input[type='text'] {
|
||||
border-radius: $border-radius;
|
||||
border-color: #0070d1;
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0rem 0.0625rem .25rem 0rem;
|
||||
border-right: none;
|
||||
border-radius: 4.5rem 0 0 4.5rem;
|
||||
height: 3rem;
|
||||
&:focus, &:focus-within {
|
||||
border-right:none;
|
||||
}
|
||||
}
|
||||
button[type='submit'] {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 0;
|
||||
background-image: url($svg-search-icon);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
border-radius: 0 $border-radius-lg $border-radius-lg 0;
|
||||
border-radius: 0 4.5rem 4.5rem 0;
|
||||
background-color: $blue-100;
|
||||
width: 2.75rem;
|
||||
height: 100%;
|
||||
border: 1px solid $gray-500;
|
||||
border: 1px solid #0070d1;
|
||||
border-left: none;
|
||||
display: flex;
|
||||
}
|
||||
|
|
@ -394,6 +402,7 @@ input::-webkit-date-and-time-value {
|
|||
&:disabled,
|
||||
&.disabled {
|
||||
background-color: $gray-100;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
p {
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export default {
|
|||
);
|
||||
}
|
||||
|
||||
if (error.response.status != '404') {
|
||||
if (error.response.status !== 404) {
|
||||
global.$logger.logError(
|
||||
`${method}: ${endpoint}: ${error.message}`,
|
||||
error.response
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
/* eslint-disable no-use-before-define */
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable jsdoc/require-param-type */
|
||||
/* eslint-disable jsdoc/require-param-description */
|
||||
/* eslint-disable jsdoc/require-returns */
|
||||
import dynamicStrings from '@/constants/dynamic-strings';
|
||||
import { useMainStore } from '@/store';
|
||||
|
||||
|
|
@ -300,7 +295,6 @@ function mapStringToState(str) {
|
|||
// Our final string value that will be built from the matches.
|
||||
const stringBuilder = '';
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const match of globalStateMatches) {
|
||||
// Reset store state for each match.
|
||||
const valueFromStore = getStoreValueFromString(match[2]);
|
||||
|
|
@ -331,12 +325,10 @@ function getStoreValueFromString(str) {
|
|||
if (!str) return '';
|
||||
|
||||
let storeOrStateObject = useMainStore();
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const s of str.split('.')) {
|
||||
if (s === 'getters') continue; // For backward compatibility
|
||||
// TODO: I don't think this next line is doing what they think it's doing.
|
||||
// eslint-disable-next-line eqeqeq, valid-typeof
|
||||
if (typeof storeOrStateObject[s] != undefined) {
|
||||
if (typeof storeOrStateObject[s] !== "undefined") {
|
||||
storeOrStateObject = storeOrStateObject[s];
|
||||
} else {
|
||||
break;
|
||||
|
|
@ -384,13 +376,11 @@ export function processIfStatements(str, ifConditionKeyword, replacePlaceholderC
|
|||
*/
|
||||
function getAndFlagFirstNonNestedIfStatementWithKeyword(matches, ifConditionKeyword) {
|
||||
let index = 0;
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const match of matches) {
|
||||
if (match.groups.isIfStatement && match.groups.ifConditionType === ifConditionKeyword) {
|
||||
let interiorIndex = 0;
|
||||
let nestedLevel = 0;
|
||||
let elseStatementIndex = null;
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const interiorMatch of matches.slice(index + 1)) {
|
||||
if (interiorMatch.groups.isIfStatement) {
|
||||
if (interiorMatch.groups.ifConditionType === ifConditionKeyword) {
|
||||
|
|
@ -539,7 +529,6 @@ export function doesCopyContainTextLink(copy) {
|
|||
export function setupModalLinks(context) {
|
||||
context.$nextTick(() => {
|
||||
const elements = document.getElementsByClassName('modal-text');
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const element of elements) {
|
||||
const target = element.getAttribute('modalTarget');
|
||||
if (target) {
|
||||
|
|
@ -626,7 +615,7 @@ export function getRouterLinkHtmlStringFromCopy(copy) {
|
|||
|
||||
/**
|
||||
* Returns a phone link as an 'a' tag element
|
||||
* @param copy
|
||||
* @param phoneNumber
|
||||
* @returns string
|
||||
*/
|
||||
export function getPhoneLinkHtmlStringFromPhoneNumber(phoneNumber) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { useMainStore } from '@/store';
|
|||
* @function isLocalhost
|
||||
*/
|
||||
function isLocalhost() {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
return location.hostname.includes('localhost');
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +29,6 @@ function getCookieValueByName(name) {
|
|||
Gets current domain without the subdomain for cookie.
|
||||
*/
|
||||
function getDomainWithoutSubdomain() {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
const url = location.hostname;
|
||||
if (isLocalhost()) {
|
||||
return 'localhost';
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ function hasMatchingReplacementOption(vehicleDamageOptions, selectedGlassToRepla
|
|||
Rear: 'backGlassOptions'
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const glassToReplace of selectedGlassToReplace) {
|
||||
const propName = optionsMap[glassToReplace.glassLocation];
|
||||
const { availableReplacementOptions } = vehicleDamageOptions[propName];
|
||||
|
|
@ -91,6 +90,15 @@ export function getGlassList(glassPieces) {
|
|||
return names.toLowerCase();
|
||||
}
|
||||
|
||||
export function includesWindshieldReplacement() {
|
||||
const mainStore = useMainStore();
|
||||
const windshieldMatches =
|
||||
mainStore.damage.glassToReplace?.filter(
|
||||
(glassToReplace) => glassToReplace.glassLocation === damageLocationsSelected.WINDSHIELD
|
||||
) ?? [];
|
||||
return windshieldMatches.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commented code are copied directly from DigitalConsumer.FixMyGlass
|
||||
* and have not been adjusted for ISS.
|
||||
|
|
|
|||
|
|
@ -134,7 +134,6 @@ export function getDateFormat(date, format) {
|
|||
const hour = (`0${date.getHours()}`).slice(-2);
|
||||
const minute = (`0${date.getMinutes()}`).slice(-2);
|
||||
const second = (`0${date.getSeconds()}`).slice(-2);
|
||||
// eslint-disable-next-line consistent-return
|
||||
return format
|
||||
.replace('yyyy', year)
|
||||
.replace('MM', month)
|
||||
|
|
@ -147,7 +146,6 @@ export function getDateFormat(date, format) {
|
|||
|
||||
export function padTo2Digits(time) {
|
||||
// Use the built-in method toString() with a radix of 10 to convert the time value to a decimal string
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
time = time.toString(10);
|
||||
// Use the conditional operator to check if the length of the string is less than 2
|
||||
return time.length < 2
|
||||
|
|
@ -172,7 +170,6 @@ export function convertMsToTime(milliseconds) {
|
|||
export function calculateDuration(startDate, endDate) {
|
||||
if (startDate instanceof Date !== true) return;
|
||||
if (endDate instanceof Date !== true) return;
|
||||
// eslint-disable-next-line consistent-return
|
||||
return convertMsToTime(endDate - startDate);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable max-len */
|
||||
import { experimentSettings } from '@/constants/experiments';
|
||||
|
||||
export function hasExperimentSetting(storeExperimentSettings, settingName) {
|
||||
|
|
|
|||
|
|
@ -125,6 +125,28 @@ function defineGlobalZipCodeRules() {
|
|||
defineRule(globalRules.ZIP_CODE_SEARCH_FORMAT, regex(/^\d{5}$/, errorMessages.ZIP_FORMAT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Define global rules related to the zip code/shop name search on TPA Search page
|
||||
*/
|
||||
function defineGlobalTpaSearchRules() {
|
||||
defineRule(globalRules.TPA_SEARCH_FORMAT, (value) => {
|
||||
if (value.length === 0) {
|
||||
return errorMessages.TPA_SEARCH_REQUIRED;
|
||||
}
|
||||
// Non Numeric
|
||||
if (isNaN(value)) {
|
||||
if (value.length < 3) {
|
||||
return errorMessages.TPA_SEARCH_SHOP_FORMAT;
|
||||
}
|
||||
} else {
|
||||
if (value.length !== 5) {
|
||||
return errorMessages.TPA_SEARCH_ZIP_FORMAT;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @function defineGlobalRules
|
||||
* @summary Define all global rules
|
||||
|
|
@ -138,6 +160,6 @@ export default function defineGlobalRules() {
|
|||
defineGlobalPolicyNumberRules();
|
||||
defineGlobalPolicyZipCodeRules();
|
||||
defineGlobalZipCodeRules();
|
||||
|
||||
defineGlobalTpaSearchRules();
|
||||
defineRule(globalRules.OPTION_REQUIRED, required(errorMessages.OPTION_REQUIRED));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function getLineItemsFlattened(lineItems) {
|
||||
return lineItems?.flatMap((li) => [li, ...(getLineItemsFlattened(li.childParts))]) ?? [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ export function deepClone(object) {
|
|||
}
|
||||
|
||||
const clone = { ...object };
|
||||
// eslint-disable-next-line no-return-assign
|
||||
Object.keys(clone).forEach((key) =>
|
||||
(clone[key] = typeof object[key] === 'object' ? deepClone(object[key]) : object[key]));
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
export function getPriceOfLineItem(lineItem) {
|
||||
let price = (lineItem.kitPrice ?? 0) + (lineItem.laborAmount ?? 0) + (lineItem.sellingPrice ?? 0);
|
||||
if (lineItem.childParts && lineItem.childParts.length !== 0) {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
price += getPriceOfLineItems(lineItem.childParts);
|
||||
}
|
||||
return price;
|
||||
|
|
@ -15,7 +14,6 @@ export function getPriceOfLineItem(lineItem) {
|
|||
export function getSalesTaxOfLineItem(lineItem) {
|
||||
let price = lineItem.salesTax ?? 0;
|
||||
if (lineItem.childParts && lineItem.childParts.length !== 0) {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
price += getTaxOfLineItems(lineItem.childParts);
|
||||
}
|
||||
return price;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ describe('querystring-helper', () => {
|
|||
const result = getLineItemQueryString(lineItems, 'param');
|
||||
|
||||
// Assert
|
||||
// eslint-disable-next-line max-len
|
||||
expect(result).toBe('¶m[0].partNumber=a¶m[1].partNumber=b¶m[2].partNumber=c¶m[3].partNumber=d¶m[4].partNumber=e');
|
||||
});
|
||||
});
|
||||
|
|
@ -74,7 +73,6 @@ describe('querystring-helper', () => {
|
|||
const result = getTaxLineItemQueryString(lineItems, 'param');
|
||||
|
||||
// Assert
|
||||
// eslint-disable-next-line max-len
|
||||
expect(result).toBe('¶m[0].partNumber=a¶m[0].laborAmount=0¶m[0].sellingPrice=10¶m[0].kitPrice=10¶m[1].partNumber=b¶m[1].laborAmount=10¶m[1].sellingPrice=10¶m[1].kitPrice=0¶m[2].partNumber=c¶m[2].laborAmount=5¶m[2].sellingPrice=5¶m[2].kitPrice=5¶m[3].partNumber=d¶m[3].laborAmount=10¶m[3].sellingPrice=10¶m[3].kitPrice=10');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export function createUnorderedListFromStringOfParagraphs(stringOfParagraphs) {
|
|||
*/
|
||||
export function toTitleCase(text) {
|
||||
let temp = text?.toLowerCase() ?? '';
|
||||
return temp.replace(/(^|\s|-)\S/g, (letter) => letter.toUpperCase());
|
||||
return temp.replace(/(^|\s|-|\/)\S/g, (letter) => letter.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -114,7 +114,7 @@ const currencyFormatter = new Intl.NumberFormat('en-US', {
|
|||
|
||||
/**
|
||||
* @function formatAmountInDollars
|
||||
* @param {string, number} amount
|
||||
* @param {string | number} amount
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatAmountInDollars(amount) {
|
||||
|
|
@ -133,3 +133,20 @@ export function formatDate(date, formatter) {
|
|||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @function toPossessive
|
||||
* @param {string | null} input
|
||||
* @returns {string}
|
||||
*/
|
||||
export function toPossessive(input) {
|
||||
if (input == null || input.trim().length === 0) {
|
||||
return input;
|
||||
}
|
||||
|
||||
if (input.toLowerCase().endsWith("s")) {
|
||||
return `${input}'`
|
||||
}
|
||||
|
||||
return `${input}'s`
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { RouterLinkStub } from '@vue/test-utils';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import navigationScenarios from '@/router/router-constants/navigation-scenarios.js';
|
||||
|
|
@ -60,9 +59,7 @@ export function getMountOptions(mockData) {
|
|||
|
||||
// Heritage integration common methods
|
||||
export const cookies = {
|
||||
[cookieNames.ISS_SESSION_INFO]:
|
||||
// eslint-disable-next-line max-len
|
||||
'{"ReferralNumber":"1566818","ReferralDate":"2022-03-15T10:56:24.597","ReferralCorrelationId":"404d2b04-f86e-45c3-b373-127b6217b060","ShouldResetState":false}',
|
||||
[cookieNames.ISS_SESSION_INFO]: '{"ReferralNumber":"1566818","ReferralDate":"2022-03-15T10:56:24.597","ReferralCorrelationId":"404d2b04-f86e-45c3-b373-127b6217b060","ShouldResetState":false}',
|
||||
UNIQUE_SESSION_ID: '33756020-b58e-4ec7-b8b8-3f1576719c40',
|
||||
anotherCookie: '{}',
|
||||
someOtherCookie: '{}',
|
||||
|
|
|
|||
|
|
@ -232,7 +232,6 @@ export default {
|
|||
const self = this;
|
||||
this.$nextTick(() => {
|
||||
this.showAllFields = true;
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
let processedStreetAddress = false;
|
||||
let processedRoute = false;
|
||||
for (const component of googlePlace.address_components) {
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ export default {
|
|||
const label = this.cartOrder.damage.glassToReplace?.length > 0
|
||||
? this.getCmsContent(this.widget.warrantyText, widgetFields.TEXT_BLOCK_WIDGET.TEXT)
|
||||
: this.getCmsContent(this.widget.guaranteeText, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
return {
|
||||
return {
|
||||
name: label,
|
||||
cartItemType: cartItemType.WARRANTY,
|
||||
partType: partTypeStrings.WARRANTY,
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, propsData
|
|||
|
||||
async function awaitingSetupTicks(wrapper) {
|
||||
for (let i = 0; i < 11; i++) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await wrapper.vm.$nextTick();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,8 +119,8 @@ export default {
|
|||
const map = await this.getMap(zipBounds?.getCenter());
|
||||
|
||||
await this.addMarkersToMap(map, markerPositions);
|
||||
let positionsToDisplay = markerPositions.map((marker) => marker.position);
|
||||
if(zipBounds) {
|
||||
const positionsToDisplay = markerPositions.map((marker) => marker.position);
|
||||
if (zipBounds) {
|
||||
positionsToDisplay.push(zipBounds.getNorthEast());
|
||||
positionsToDisplay.push(zipBounds.getSouthWest());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable max-len */
|
||||
// Components
|
||||
import questionsPageLayout from '@/iss-components/questions-page-layout/questions-page-layout.vue';
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<div class="iss-heritage-container-width">
|
||||
<div class="questions-page-layout-container iss-heritage-content-container-width">
|
||||
<siteSubHeader
|
||||
class="mt-4"
|
||||
class="mt-4 subheader"
|
||||
cmsWidgetName="SiteSubHeaderWidget" />
|
||||
<alert
|
||||
v-if="alertFewMoreQuestionsHeader || alertFewMoreQuestionsCopy"
|
||||
|
|
@ -129,6 +129,13 @@ export default {
|
|||
padding-left: .9375rem;
|
||||
padding-right: .9375rem;
|
||||
}
|
||||
.subheader {
|
||||
:deep(strong) {
|
||||
color: $black;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.questions-page {
|
||||
|
|
@ -141,6 +148,7 @@ export default {
|
|||
}
|
||||
}
|
||||
.need-help-link-container {
|
||||
margin-top: 1.875rem;
|
||||
margin-bottom: 2.25rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -123,7 +123,6 @@ $heritage-checked-border-color: #0070d1;
|
|||
&:checked + .list-button-content {
|
||||
background: $background-color-selected;
|
||||
border-color: $heritage-checked-border-color;
|
||||
box-shadow: 0 0 0 1px $blue;
|
||||
.button-label-copy {
|
||||
font-weight: 500;
|
||||
color: $black;
|
||||
|
|
|
|||
|
|
@ -14,14 +14,13 @@
|
|||
id="stacked"
|
||||
class="col button-col d-flex px-0">
|
||||
<buttonMain
|
||||
v-if="!isForwardButtonHidden"
|
||||
ref="buttonMain"
|
||||
variant="navigation"
|
||||
:buttonText="buttonText"
|
||||
:class="
|
||||
(disableForwardAction || isForwardActionDisabled) &&
|
||||
'form-test-invalid'
|
||||
"
|
||||
:class="{
|
||||
'form-test-invalid': (disableForwardAction || isForwardActionDisabled),
|
||||
'hide-button': isForwardButtonHidden
|
||||
}"
|
||||
:aria-disabled="disableForwardAction || isForwardActionDisabled"
|
||||
:isDisabled="disableForwardAction || isForwardActionDisabled"
|
||||
data-test-id="site-footer-main-button"
|
||||
|
|
@ -149,5 +148,8 @@ export default {
|
|||
margin-top: 1.25rem;
|
||||
}
|
||||
}
|
||||
.hide-button {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ export default {
|
|||
alternateFormatting() {
|
||||
// override for service-packages unique style
|
||||
if (this.issContainingPage?.toLowerCase() === 'service-packages') {
|
||||
return 'service-packages-subtext my-4';
|
||||
return 'service-packages-subtext';
|
||||
}
|
||||
return this.darkGraySubText ? 'dark-gray' : 'light-gray';
|
||||
}
|
||||
|
|
@ -169,8 +169,8 @@ p {
|
|||
color: $darker-gray;
|
||||
}
|
||||
&.service-packages-subtext {
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
line-height: 26px;
|
||||
span {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
|
|
|||
117
src/iss-components/tpa-shop-list-button/tpa-shop-list-button.vue
Normal file
117
src/iss-components/tpa-shop-list-button/tpa-shop-list-button.vue
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<transition
|
||||
name="fade"
|
||||
mode="out-in">
|
||||
<baseInputButton
|
||||
v-bind="$props"
|
||||
v-model="selectedValue"
|
||||
buttonWrapperClasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 label-margin-bottom no-hover">
|
||||
<div
|
||||
:aria-label="buttonLabel"
|
||||
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4">
|
||||
<div class="row-one">
|
||||
<span
|
||||
id="buttonLabelSpan"
|
||||
class="m-0 button-label-copy"
|
||||
:class="textPosition">{{ buttonLabel }}
|
||||
</span>
|
||||
<span
|
||||
id="buttonLabelSubCopySpan"
|
||||
class="m-0 caption ms-2"
|
||||
:class="textPosition">{{ buttonLabelSubCopy }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row-two">
|
||||
<span
|
||||
v-if="buttonBodyCopy"
|
||||
id="buttonBodyCopy"
|
||||
class="m-0 button-label-sub-copy"
|
||||
v-html="buttonBodyCopy"></span>
|
||||
</div>
|
||||
<span
|
||||
v-if="screenReaderOnlyText"
|
||||
id="screenReaderOnlyTextSpan"
|
||||
class="sr-only">
|
||||
{{ screenReaderOnlyText }}
|
||||
</span>
|
||||
</div>
|
||||
</baseInputButton>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import baseInputButton from '@/digital-components/base-input-button/base-input-button.vue';
|
||||
import inputButtonWrapperMixin from '@/mixins/input-button-wrapper-mixin';
|
||||
|
||||
export default {
|
||||
name: 'tpa-shop-list-button',
|
||||
components: {
|
||||
baseInputButton
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin],
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/styles/ux-variables-svg-strings.scss";
|
||||
|
||||
.list-button {
|
||||
outline: none;
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
position: static; //override bootstrap
|
||||
|
||||
&:checked + .list-button-content {
|
||||
background: #e7f1f6;
|
||||
border: 1px solid #0070d1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-button-content {
|
||||
color: $gray-600;
|
||||
position: relative;
|
||||
background: $white;
|
||||
transition: all 150ms linear;
|
||||
border-radius: $border-radius-lg;
|
||||
border: 1px solid $gray-500;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
|
||||
span {
|
||||
&.small {
|
||||
font-size: 0.75rem;
|
||||
color: $gray-550;
|
||||
}
|
||||
}
|
||||
}
|
||||
.button-content {
|
||||
row-gap: 0.25rem;
|
||||
color: #4d4e53;
|
||||
font-size: .9375rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.625rem;
|
||||
|
||||
.row-one {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 1.5rem;
|
||||
|
||||
.button-label-copy {
|
||||
font-weight: 600;
|
||||
line-height: 1.5rem;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
#buttonLabelSubCopySpan {
|
||||
font-size: .75rem;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.row-two {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
div.col:not(:last-child) >.label-margin-bottom{
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -167,9 +167,7 @@ describe('address-lookup.vue', () => {
|
|||
expect(wrapper.findComponent({ ref: 'alertMatchedTwoIdenticalYMMVehicle' }).isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
test(
|
||||
'if the looking up VIN by address is not allowed in the state selected display the Vin Lookup By HomeAddress Not Allowed Alert',
|
||||
test('if the looking up VIN by address is not allowed in the state selected display the Vin Lookup By HomeAddress Not Allowed Alert',
|
||||
async () => {
|
||||
// Arrange
|
||||
const mockRegistrationAddress = {
|
||||
|
|
@ -364,9 +362,7 @@ describe('address-lookup.vue', () => {
|
|||
);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
test(
|
||||
'if a different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page',
|
||||
test('if a different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page',
|
||||
async () => {
|
||||
// Arrange
|
||||
const mockRegistrationAddress = {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,11 @@
|
|||
ref="siteSubHeader"
|
||||
cmsWidgetName="SiteSubHeaderWidget"
|
||||
class="mt-4" />
|
||||
<customerQuestions
|
||||
ref="customerQuestions"
|
||||
v-model="customerQuestions" />
|
||||
<div class="mt-4">
|
||||
<customerQuestions
|
||||
ref="customerQuestions"
|
||||
v-model="customerQuestions" />
|
||||
</div>
|
||||
<alert
|
||||
v-if="displayVinNotFoundAlert"
|
||||
ref="alertVinNotFound"
|
||||
|
|
@ -107,7 +109,6 @@ export default {
|
|||
siteSubHeader,
|
||||
customerQuestions,
|
||||
alert,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
},
|
||||
mixins: [baseFormMixin, vinPagesMixin],
|
||||
|
|
@ -153,9 +154,7 @@ export default {
|
|||
).replaceAll('{custom:damage}', getDamageString());
|
||||
},
|
||||
AlertMatchedDifferentVehicleBody() {
|
||||
const vinYmmFound =
|
||||
// eslint-disable-next-line max-len
|
||||
`${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
|
||||
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
|
||||
const vinYmmExpected = `${useMainStore().order.vehicle.year} ${
|
||||
useMainStore().order.vehicle.make
|
||||
} ${useMainStore().order.vehicle.model}`;
|
||||
|
|
@ -175,12 +174,8 @@ export default {
|
|||
).replaceAll('{custom:damage}', getDamageString());
|
||||
},
|
||||
AlertMatchedTwoIdenticalYMMVehicleBody() {
|
||||
const vinYmmsFound =
|
||||
// eslint-disable-next-line max-len
|
||||
`${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model} ${this.customAlertData?.vehicleInfo?.style}`;
|
||||
const vinYmmsExpected =
|
||||
// eslint-disable-next-line max-len
|
||||
`${useMainStore().order.vehicle.year} ${useMainStore().order.vehicle.make} ${useMainStore().order.vehicle.model} ${useMainStore().order.vehicle.style}`;
|
||||
const vinYmmsFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model} ${this.customAlertData?.vehicleInfo?.style}`;
|
||||
const vinYmmsExpected = `${useMainStore().order.vehicle.year} ${useMainStore().order.vehicle.make} ${useMainStore().order.vehicle.model} ${useMainStore().order.vehicle.style}`;
|
||||
|
||||
return this.getCmsContent(
|
||||
'AlertMatchedTwoIdenticalYMMVehicleWidget',
|
||||
|
|
@ -191,9 +186,7 @@ export default {
|
|||
.replaceAll('{custom:vinYmmsExpected}', vinYmmsExpected);
|
||||
},
|
||||
isTwoIdenticalYMMVehicleFound() {
|
||||
const vinYmmFound =
|
||||
// eslint-disable-next-line max-len
|
||||
`${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
|
||||
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
|
||||
const vinYmmExpected = `${useMainStore().order.vehicle.year} ${
|
||||
useMainStore().order.vehicle.make
|
||||
} ${useMainStore().order.vehicle.model}`;
|
||||
|
|
@ -290,9 +283,7 @@ export default {
|
|||
|
||||
// Update button "Continue with..."
|
||||
showIssLoadingModal(false);
|
||||
return this.$refs.siteFooter
|
||||
// eslint-disable-next-line max-len
|
||||
.updateButtonText(`Continue with ${carFound.year} ${carFound.make} ${carFound.model} ${this.forwardButtonCarStyle}`);
|
||||
return this.$refs.siteFooter.updateButtonText(`Continue with ${carFound.year} ${carFound.make} ${carFound.model} ${this.forwardButtonCarStyle}`);
|
||||
}
|
||||
|
||||
// update data
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export default {
|
|||
vehicles: Array,
|
||||
vehicleSelected: Object,
|
||||
modelValue: String,
|
||||
cmsWidgetName: String,
|
||||
questionText: String,
|
||||
validationRules: String,
|
||||
isCarIdDifferent: Boolean,
|
||||
displayMatchedDifferentVehicleAlert: Boolean,
|
||||
|
|
@ -81,20 +81,14 @@ export default {
|
|||
).replaceAll('{custom:damage}', getDamageString());
|
||||
},
|
||||
AlertMatchedTwoIdenticalYMMVehicleBody() {
|
||||
const vinYmmsFound =
|
||||
// eslint-disable-next-line max-len
|
||||
`${this.selectedVehicle?.vehicle.year} ${this.selectedVehicle?.vehicle.make} ${this.selectedVehicle?.vehicle.model} ${this.selectedVehicle?.vehicle.style}`;
|
||||
const vinYmmsExpected =
|
||||
`${this.vehicleSelected?.year} ${this.vehicleSelected?.make} ${this.vehicleSelected?.model} ${this.vehicleSelected?.style}`;
|
||||
const vinYmmsFound = `${this.selectedVehicle?.vehicle.year} ${this.selectedVehicle?.vehicle.make} ${this.selectedVehicle?.vehicle.model} ${this.selectedVehicle?.vehicle.style}`;
|
||||
const vinYmmsExpected = `${this.vehicleSelected?.year} ${this.vehicleSelected?.make} ${this.vehicleSelected?.model} ${this.vehicleSelected?.style}`;
|
||||
|
||||
return this.getCmsContent('AlertMatchedTwoIdenticalYMMVehicleWidget', 'BodyText')
|
||||
.replaceAll('{custom:damage}', getDamageString())
|
||||
.replaceAll('{custom:vinYmmsFound}', vinYmmsFound)
|
||||
.replaceAll('{custom:vinYmmsExpected}', vinYmmsExpected);
|
||||
},
|
||||
questionText() {
|
||||
return this.getCmsContent('VehicleConfirmationQuestion', 'QuestionText');
|
||||
},
|
||||
selectedVehicleVin: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
|
|
@ -126,4 +120,8 @@ export default {
|
|||
#address-vehicles-question-alert p {
|
||||
margin-bottom: 0 !important; // Overrides extra margin-bottom on alert body text
|
||||
}
|
||||
|
||||
:deep(div.list-button-content > span:first-child) {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@ function setupMocks({
|
|||
if (contentName === 'ProvideVinAlert') {
|
||||
return 'ProvideVinAlertTestReturn';
|
||||
}
|
||||
if (contentName === 'VehicleConfirmationQuestion') {
|
||||
return 'VehicleConfirmationQuestionTestReturn';
|
||||
}
|
||||
return null;
|
||||
})
|
||||
},
|
||||
|
|
@ -190,9 +193,7 @@ describe('address-vehicles.vue', () => {
|
|||
expect(wrapper.vm.forwardButtonAction).toReturn;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
test(
|
||||
'Should navigate to CLICKED_FORWARD scenario if carId is different and selected glass not available for vehicle on navigateForward',
|
||||
test('Should navigate to CLICKED_FORWARD scenario if carId is different and selected glass not available for vehicle on navigateForward',
|
||||
async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
|
|
|||
|
|
@ -13,15 +13,6 @@
|
|||
<siteSubHeader
|
||||
cmsWidgetName="SiteSubHeaderWidget"
|
||||
class="mt-4" />
|
||||
<alert
|
||||
id="multiple-vehicles-alert"
|
||||
ref="alertFoundMultipleVehicles"
|
||||
cmsWidgetName="FoundMultipleVehicles"
|
||||
class="my-5"
|
||||
alertClass="alert-warning"
|
||||
:manualHeadline="AlertFoundMultipleVehiclesHeader"
|
||||
manualCopy=""
|
||||
:isDismissible="false" />
|
||||
<alert
|
||||
v-if="displayNoServiceAlert"
|
||||
ref="AlertNoService"
|
||||
|
|
@ -32,7 +23,7 @@
|
|||
<addressVehiclesQuestion
|
||||
ref="addressVehiclesQuestion"
|
||||
v-model="selectedVehicleVin"
|
||||
cmsWidgetName="VehicleConfirmationQuestion"
|
||||
:questionText="questionText"
|
||||
:vehicles="VehiclesForQuestions"
|
||||
:vehicleSelected="VehicleSelected"
|
||||
validationRules="vehicle-required"
|
||||
|
|
@ -118,7 +109,6 @@ export default {
|
|||
siteFooter,
|
||||
siteHeader,
|
||||
siteSubHeader,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
alert,
|
||||
addressVehiclesQuestion
|
||||
|
|
@ -165,10 +155,10 @@ export default {
|
|||
vehicleCount() {
|
||||
return this.VehiclesForQuestions.length;
|
||||
},
|
||||
AlertFoundMultipleVehiclesHeader() {
|
||||
questionText() {
|
||||
return this.getCmsContent(
|
||||
'FoundMultipleVehicles',
|
||||
'HeadlineText'
|
||||
'VehicleConfirmationQuestion',
|
||||
'QuestionText'
|
||||
).replaceAll('{custom:vehicleCount}', this.vehicleCount);
|
||||
},
|
||||
isTwoIdenticalYMMVehicleFound() {
|
||||
|
|
@ -187,7 +177,7 @@ export default {
|
|||
// Map API result data, to address-vehicles data structure
|
||||
const mappedData = this.VehiclesFromApi.map((v) => {
|
||||
const maskSymbol = '*';
|
||||
const vinStart = maskSymbol.repeat(v.vin.length - 6);
|
||||
const vinStart = maskSymbol.repeat(6);
|
||||
const vinEnd = v.vin.substring(v.vin.length - 6);
|
||||
return {
|
||||
vin: v.vin,
|
||||
|
|
@ -235,13 +225,6 @@ export default {
|
|||
} else {
|
||||
this.displayMatchedDifferentVehicleAlert = true;
|
||||
}
|
||||
this.$refs.siteFooter.updateButtonText(`Continue with ${this.selectedVehicle.vehicle.year} `
|
||||
+ `${this.selectedVehicle.vehicle.make} ${this.selectedVehicle.vehicle.model} ${this.forwardButtonCarStyle}`);
|
||||
} else {
|
||||
this.$refs.siteFooter.updateButtonText(this.getCmsContent(
|
||||
'SiteFooterWidget',
|
||||
'ForwardButtonText'
|
||||
));
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ export default {
|
|||
siteSubHeader,
|
||||
siteFooter,
|
||||
textboxQuestion,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
textBlock
|
||||
},
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ const baseStoreGettersPageData = () => ({
|
|||
capabilityQuestions: [
|
||||
{
|
||||
questionSequence: 1,
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with the optional Lane-Keeping System which tugs on the steering wheel and/or beeps to alert you if you drift too close to the edge of the lane?',
|
||||
questionText: 'Is your vehicle equipped with the optional Lane-Keeping System which tugs on the steering wheel and/or beeps to alert you if you drift too close to the edge of the lane?',
|
||||
answers: [
|
||||
{
|
||||
answerResult1: 'DYNAMIC',
|
||||
|
|
@ -67,17 +65,13 @@ const baseStoreGettersDamage = () => ({
|
|||
result: 'FW04848',
|
||||
answeredQuestions: [
|
||||
{
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
questionText: 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
selectedAnswer: '1|nextQuestion|3|Yes',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 1
|
||||
},
|
||||
{
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
questionText: 'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
selectedAnswer: '2|nextQuestion|3|Yes',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 2
|
||||
|
|
@ -212,17 +206,13 @@ describe('capabilityQuestions.vue', () => {
|
|||
answerResult: 'FW04848',
|
||||
answeredQuestions: [
|
||||
{
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
questionText: 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
selectedAnswer: '1|nextQuestion|3|Yes',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 1
|
||||
},
|
||||
{
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
questionText: 'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
selectedAnswer: '2|nextQuestion|3|Yes',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 2
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import questionsPageLayout from '@/iss-components/questions-page-layout/question
|
|||
export default {
|
||||
name: 'capability-questions',
|
||||
components: {
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
questionsPageLayout
|
||||
},
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
inputId="state"
|
||||
class="form-group"
|
||||
:cmsWidgetName="widget.stateQuestion"
|
||||
:options="states"
|
||||
:options="getStates"
|
||||
isDisabled />
|
||||
<textboxQuestion
|
||||
ref="zipCodeQuestion"
|
||||
|
|
@ -135,8 +135,8 @@ import widgetFields from '@/constants/cms-widget-fields';
|
|||
import states from '@/constants/states';
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule('street-address-required', required(errorMessages.STREET_ADDRESS_REQUIRED));
|
||||
defineRule('city-required', required(errorMessages.CITY_REQUIRED));
|
||||
defineRule('street-address-required', required(errorMessages.SERVICE_ADDRESS_REQUIRED));
|
||||
defineRule('city-required', required(errorMessages.SERVICE_CITY_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: 'contact-details',
|
||||
|
|
@ -147,7 +147,6 @@ export default {
|
|||
checkbox,
|
||||
textareaQuestion,
|
||||
siteFooter,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
alert,
|
||||
buttonQuestion,
|
||||
|
|
@ -219,7 +218,13 @@ export default {
|
|||
},
|
||||
vehicleProtectedQuestionAnswers() {
|
||||
return this.getCmsContent(this.widget.vehicleProtectedQuestion, widgetFields.INPUT_QUESTION_WIDGET.ANSWERS) || [];
|
||||
}
|
||||
},
|
||||
getStates() {
|
||||
return Object.keys(states).reduce((acc, key) => {
|
||||
acc[key] = states[key].toUpperCase();
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isSameAsPolicyAddress(newValue) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<transition
|
||||
name="fade"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable max-len */
|
||||
// Components
|
||||
import coverageStatement from '@/layouts/coverage-statement/coverage-statement.vue';
|
||||
|
||||
|
|
@ -368,7 +367,6 @@ describe('coverageStatement.vue', () => {
|
|||
[false, coverageStatuses.NO_COVERAGE, coverageType.ITAC],
|
||||
[false, coverageStatuses.NO_COVERAGE, coverageType.Deductible]
|
||||
])('isQuoteDisplayed', (expected, status, type) => {
|
||||
// eslint-disable-next-line max-len
|
||||
test(`$returns ${expected} when coverageStatus is ${getEnumName(coverageStatuses, status)} and coverageType is ${getEnumName(coverageType, type)}`, () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
|
|
@ -726,7 +724,6 @@ describe('coverageStatement.vue', () => {
|
|||
// Act
|
||||
coverageStatement.beforeRouteEnter.call(wrapper.vm, undefined, undefined, next);
|
||||
for (let i = 0; i < 7; i++) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await nextTick();
|
||||
}
|
||||
|
||||
|
|
@ -752,7 +749,6 @@ describe('coverageStatement.vue', () => {
|
|||
// Act
|
||||
coverageStatement.beforeRouteEnter.call(wrapper.vm, undefined, undefined, next);
|
||||
for (let i = 0; i < 7; i++) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await nextTick();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<Form
|
||||
ref="theForm"
|
||||
v-slot="{ meta }"
|
||||
@submit="onSubmit"
|
||||
@invalidSubmit="onInvalidSubmit">
|
||||
<div class="fade-on-route-transition coverage-statement">
|
||||
|
|
@ -147,7 +145,6 @@ export default {
|
|||
name: 'coverage-statement',
|
||||
components: {
|
||||
siteHeader,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
contentGroupModal,
|
||||
buttonMain,
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ describe('duplicateCheck.vue', () => {
|
|||
expect(wrapper.vm.$router.navigate)
|
||||
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_POLICY_UNVERIFIED, undefined);
|
||||
});
|
||||
// eslint-disable-next-line max-len
|
||||
|
||||
test('coverageType deductible and loaded duplicate with policy vehicle => CLICKED_FORWARD_LOADED_DUPLICATE_WITH_POLICY_VEHICLE', async () => {
|
||||
// Arrange
|
||||
const vin = getRandomString(17, 17);
|
||||
|
|
@ -454,7 +454,7 @@ describe('duplicateCheck.vue', () => {
|
|||
expect(wrapper.vm.$router.navigate)
|
||||
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_LOADED_DUPLICATE_WITH_POLICY_VEHICLE, undefined);
|
||||
});
|
||||
// eslint-disable-next-line max-len
|
||||
|
||||
test('coverageType deductible and loaded duplicate with non policy vehicle => CLICKED_FORWARD_LOADED_DUPLICATE_WITH_NON_POLICY_VEHICLE', async () => {
|
||||
// Arrange
|
||||
const vin = getRandomString(17, 17);
|
||||
|
|
@ -482,7 +482,7 @@ describe('duplicateCheck.vue', () => {
|
|||
expect(wrapper.vm.$router.navigate)
|
||||
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_LOADED_DUPLICATE_WITH_NON_POLICY_VEHICLE, undefined);
|
||||
});
|
||||
// eslint-disable-next-line max-len
|
||||
|
||||
test('coverageType deductible and loaded duplicate with no policy vehicles => CLICKED_FORWARD_LOADED_DUPLICATE_WITH_NO_POLICY_VEHICLES', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent({
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ export default {
|
|||
siteSubHeader,
|
||||
buttonQuestion,
|
||||
siteFooter,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
buttonMain
|
||||
},
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ describe('entry-page.vue', () => {
|
|||
TPAEnabled: true,
|
||||
ClientFullName: 'Full Name',
|
||||
ClientDisplayName: 'Display Name',
|
||||
ClientPossessiveName: 'Display Name\'s',
|
||||
ClaimRegistrationRequired: true,
|
||||
EnableNoCompQuote: true
|
||||
})
|
||||
|
|
@ -153,6 +154,7 @@ describe('entry-page.vue', () => {
|
|||
expect(issConfig.clientName).toBe('TestClient');
|
||||
expect(issConfig.clientFullName).toBe('Full Name');
|
||||
expect(issConfig.clientDisplayName).toBe('Display Name');
|
||||
expect(issConfig.clientPossessiveName).toBe('Display Name\'s');
|
||||
expect(issConfig.parentAccountNumber).toBe('12345');
|
||||
expect(issConfig.styleSheet).toBe('test-style');
|
||||
expect(issConfig.isCoverageEnabled).toBe(true);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { getISSCookie, updateOrCreateISSCookie } from '@/helpers/cookie-helper.j
|
|||
import { useMainStore } from '@/store';
|
||||
import showIssLoadingModal from '@/helpers/loading-modal-helper';
|
||||
import applicationConfig from '@/constants/application-config';
|
||||
import { toPossessive } from '@/helpers/text-helper';
|
||||
|
||||
export default {
|
||||
name: 'entry-page',
|
||||
|
|
@ -144,6 +145,7 @@ export default {
|
|||
this.mainStore.issConfig.clientName = data.accountName;
|
||||
this.mainStore.issConfig.clientFullName = data.accountName; // Defaults to use the client name.
|
||||
this.mainStore.issConfig.clientDisplayName = data.accountName; // Defaults to use the client name.
|
||||
this.mainStore.issConfig.clientPossessiveName = toPossessive(data.accountName); // Defaults to use the client name.
|
||||
this.mainStore.issConfig.parentAccountNumber = data.parentAccountNumber;
|
||||
this.mainStore.issConfig.styleSheet = data.styleSheet;
|
||||
this.mainStore.issConfig.isCoverageEnabled = data.coverageEnabled;
|
||||
|
|
@ -163,6 +165,11 @@ export default {
|
|||
|
||||
if (clientFlags.ClientDisplayName != null) {
|
||||
this.mainStore.issConfig.clientDisplayName = clientFlags.ClientDisplayName;
|
||||
this.mainStore.issConfig.clientPossessiveName = toPossessive(clientFlags.ClientDisplayName);
|
||||
}
|
||||
|
||||
if (clientFlags.ClientPossessiveName != null) {
|
||||
this.mainStore.issConfig.clientPossessiveName = clientFlags.ClientPossessiveName;
|
||||
}
|
||||
|
||||
if (clientFlags.ClaimRegistrationRequired) {
|
||||
|
|
|
|||
|
|
@ -256,7 +256,6 @@ describe('license-plate-lookup.vue', () => {
|
|||
expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len, function-paren-newline
|
||||
test('if a different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page',
|
||||
async () => {
|
||||
// Arrange
|
||||
|
|
|
|||
|
|
@ -14,6 +14,24 @@
|
|||
cmsWidgetName="SiteSubHeaderWidget"
|
||||
class="mt-4"
|
||||
subHeaderMarginClasses="mt-1" />
|
||||
<textboxQuestion
|
||||
id="license-plate-question-wrapper"
|
||||
v-model="licensePlate"
|
||||
cmsWidgetName="LicensePlateNumberQuestionWidget"
|
||||
isRequired
|
||||
disableAutoFill
|
||||
inputId="license-plate-question"
|
||||
class="mt-4"
|
||||
validationRules="license-plate-required" />
|
||||
<dropdownQuestion
|
||||
ref="state"
|
||||
v-model="licenseState"
|
||||
cmsWidgetName="StateQuestionWidget"
|
||||
inputId="8fdf9dc2e13e430eb57529499dceb3eb"
|
||||
:options="stateOptions"
|
||||
disableAutoFill
|
||||
validationRules="state-required"
|
||||
class="mt-4" />
|
||||
<alert
|
||||
v-if="displayVinNotFoundAlert"
|
||||
ref="alertVinNotFound"
|
||||
|
|
@ -48,24 +66,6 @@
|
|||
cmsWidgetName="AlertNoServiceWidget"
|
||||
alertClass="alert-danger"
|
||||
:isDismissable="false" />
|
||||
<textboxQuestion
|
||||
id="license-plate-question-wrapper"
|
||||
v-model="licensePlate"
|
||||
cmsWidgetName="LicensePlateNumberQuestionWidget"
|
||||
isRequired
|
||||
disableAutoFill
|
||||
inputId="license-plate-question"
|
||||
class="mt-4"
|
||||
validationRules="license-plate-required" />
|
||||
<dropdownQuestion
|
||||
ref="state"
|
||||
v-model="licenseState"
|
||||
cmsWidgetName="StateQuestionWidget"
|
||||
inputId="8fdf9dc2e13e430eb57529499dceb3eb"
|
||||
:options="stateOptions"
|
||||
disableAutoFill
|
||||
validationRules="state-required"
|
||||
class="mt-4" />
|
||||
<siteFooter
|
||||
ref="siteFooter"
|
||||
class="mt-5"
|
||||
|
|
@ -115,7 +115,6 @@ defineRule('state-required', required(errorMessages.STATE_REQUIRED));
|
|||
export default {
|
||||
name: 'license-plate-lookup',
|
||||
components: {
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
siteFooter,
|
||||
siteHeader,
|
||||
|
|
@ -169,11 +168,8 @@ export default {
|
|||
).replaceAll('{custom:damage}', getDamageString());
|
||||
},
|
||||
AlertMatchedDifferentVehicleBody() {
|
||||
const vinYmmFound =
|
||||
// eslint-disable-next-line max-len
|
||||
`${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
|
||||
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make}
|
||||
${this.mainStore.order.vehicle.model}`;
|
||||
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
|
||||
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
|
||||
|
||||
return this.getCmsContent(
|
||||
'AlertMatchedDifferentVehicleWidget',
|
||||
|
|
@ -190,12 +186,8 @@ export default {
|
|||
).replaceAll('{custom:damage}', getDamageString());
|
||||
},
|
||||
AlertMatchedTwoIdenticalYMMVehicleBody() {
|
||||
const vinYmmsFound =
|
||||
// eslint-disable-next-line max-len
|
||||
`${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model} ${this.customAlertData?.vehicleInfo?.style}`;
|
||||
const vinYmmsExpected =
|
||||
// eslint-disable-next-line max-len
|
||||
`${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model} ${this.mainStore.order.vehicle.style}`;
|
||||
const vinYmmsFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model} ${this.customAlertData?.vehicleInfo?.style}`;
|
||||
const vinYmmsExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model} ${this.mainStore.order.vehicle.style}`;
|
||||
|
||||
return this.getCmsContent(
|
||||
'AlertMatchedTwoIdenticalYMMVehicleWidget',
|
||||
|
|
@ -206,9 +198,7 @@ export default {
|
|||
.replaceAll('{custom:vinYmmsExpected}', vinYmmsExpected);
|
||||
},
|
||||
isTwoIdenticalYMMVehicleFound() {
|
||||
const vinYmmFound =
|
||||
// eslint-disable-next-line max-len
|
||||
`${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
|
||||
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
|
||||
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make}
|
||||
${this.mainStore.order.vehicle.model}`;
|
||||
return vinYmmFound.toLowerCase() === vinYmmExpected.toLowerCase();
|
||||
|
|
@ -300,9 +290,7 @@ export default {
|
|||
showIssLoadingModal(false);
|
||||
|
||||
// Update button "Continue with..."
|
||||
return this.$refs.siteFooter
|
||||
// eslint-disable-next-line max-len
|
||||
.updateButtonText(`Continue with ${vehicleFromLookup.year} ${vehicleFromLookup.make} ${vehicleFromLookup.model} ${this.forwardButtonCarStyle}`);
|
||||
return this.$refs.siteFooter.updateButtonText(`Continue with ${vehicleFromLookup.year} ${vehicleFromLookup.make} ${vehicleFromLookup.model} ${this.forwardButtonCarStyle}`);
|
||||
}
|
||||
|
||||
// Save vehicle, license plate, and registration information
|
||||
|
|
|
|||
|
|
@ -132,17 +132,13 @@ const baseStoreGettersDamage = () => ({
|
|||
result: 'FW04848',
|
||||
answeredQuestions: [
|
||||
{
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
questionText: 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
selectedAnswer: '1|nextQuestion|3|Yes',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 1
|
||||
},
|
||||
{
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
questionText: 'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
selectedAnswer: '2|nextQuestion|3|Yes',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 2
|
||||
|
|
@ -219,17 +215,13 @@ describe('moldingQuestions.vue', () => {
|
|||
answerResult: 'FW04848',
|
||||
answeredQuestions: [
|
||||
{
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
questionText: 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
selectedAnswer: '1|nextQuestion|3|Yes',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 1
|
||||
},
|
||||
{
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
questionText: 'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
selectedAnswer: '2|nextQuestion|3|Yes',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 2
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ import questionsPageLayout from '@/iss-components/questions-page-layout/question
|
|||
export default {
|
||||
name: 'molding-questions',
|
||||
components: {
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
questionsPageLayout
|
||||
},
|
||||
|
|
@ -150,7 +149,6 @@ export default {
|
|||
|
||||
// get parts from the questionAnswers
|
||||
const partsOrQuestions = this.partsOrQuestionsData;
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const answer of questionAnswersArray) {
|
||||
partsOrQuestions.find((partOrQuestion) =>
|
||||
partOrQuestion.glassLocation === answer.glassLocation
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<template>
|
||||
<div
|
||||
v-if="open"
|
||||
|
|
|
|||
|
|
@ -138,7 +138,6 @@ import coverageType from '@/constants/coverage-type';
|
|||
export default {
|
||||
name: 'order-confirmation',
|
||||
components: {
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
siteHeader,
|
||||
siteFooter,
|
||||
|
|
@ -239,7 +238,7 @@ export default {
|
|||
},
|
||||
orderConfirmationUpdateAppointmentText() {
|
||||
let content = '';
|
||||
if(this.isNoComp) {
|
||||
if (this.isNoComp) {
|
||||
content = this.getCmsContentWithCustomValues(
|
||||
this.widgets.emailConfirmationNoComp,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
|
||||
|
|
|
|||
|
|
@ -90,9 +90,7 @@ const baseStoreGettersPageData = () => ({
|
|||
partQuestions: [
|
||||
{
|
||||
questionSequence: 1,
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
questionText: 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
answers: [
|
||||
{
|
||||
answerResult: '',
|
||||
|
|
@ -122,16 +120,12 @@ const baseStoreGettersDamage = () => ({
|
|||
result: 'FW04848',
|
||||
answeredQuestions: [
|
||||
{
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
questionText: 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 1
|
||||
},
|
||||
{
|
||||
questionText:
|
||||
// eslint-disable-next-line max-len
|
||||
'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
questionText: 'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 2
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ import navigationScenarios from '@/router/router-constants/navigation-scenarios'
|
|||
export default {
|
||||
name: 'part-questions',
|
||||
components: {
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
questionsPageLayout,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
import paymentMethodListButton from '@/layouts/payment-method/payment-method-question/payment-method-list-button/payment-method-list-button.vue';
|
||||
|
||||
const testConstants = {
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ import { supportsApplePay } from '@/helpers/browser-helper';
|
|||
export default {
|
||||
name: 'payment-method',
|
||||
components: {
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
siteHeader,
|
||||
siteSubHeader,
|
||||
|
|
|
|||
|
|
@ -415,7 +415,6 @@ export default {
|
|||
cartDropdown,
|
||||
siteHeader,
|
||||
siteFooter,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
alert
|
||||
},
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import showIssLoadingModal from '@/helpers/loading-modal-helper';
|
|||
export default {
|
||||
name: 'payment-return',
|
||||
components: {
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
},
|
||||
mixins: [BaseFormMixin],
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ export default {
|
|||
siteSubHeader,
|
||||
siteFooter,
|
||||
buttonQuestion,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
},
|
||||
mixins: [BaseFormMixin],
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ export default {
|
|||
textboxQuestion,
|
||||
addressQuestions,
|
||||
siteFooter,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
},
|
||||
mixins: [BaseFormMixin],
|
||||
|
|
|
|||
|
|
@ -93,10 +93,7 @@ describe('policy-vehicles.vue', () => {
|
|||
});
|
||||
|
||||
describe('forwardButtonAction', () => {
|
||||
// eslint-disable-next-line max-len
|
||||
test(
|
||||
// eslint-disable-next-line max-len
|
||||
'Selected VIN matches vehicle listed in system => update vehicle and navigate forward with CLICKED_FORWARD_LISTED_VEHICLE scenario.',
|
||||
test('Selected VIN matches vehicle listed in system => update vehicle and navigate forward with CLICKED_FORWARD_LISTED_VEHICLE scenario.',
|
||||
async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
|
@ -150,9 +147,7 @@ describe('policy-vehicles.vue', () => {
|
|||
}
|
||||
);
|
||||
|
||||
test(
|
||||
// eslint-disable-next-line max-len
|
||||
'Selected VIN matches vehicle listed in system and policy vehicle has Educator endorsement => update vehicle and navigate forward with CLICKED_FORWARD_WITH_ENDORSEMENTS scenario.',
|
||||
test('Selected VIN matches vehicle listed in system and policy vehicle has Educator endorsement => update vehicle and navigate forward with CLICKED_FORWARD_WITH_ENDORSEMENTS scenario.',
|
||||
async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
|
@ -203,9 +198,7 @@ describe('policy-vehicles.vue', () => {
|
|||
}
|
||||
);
|
||||
|
||||
test(
|
||||
// eslint-disable-next-line max-len
|
||||
'Selected VIN matches vehicle listed in system and policy vehicle has Parking Guard endorsement => update vehicle and navigate forward with CLICKED_FORWARD_WITH_ENDORSEMENTS scenario.',
|
||||
test('Selected VIN matches vehicle listed in system and policy vehicle has Parking Guard endorsement => update vehicle and navigate forward with CLICKED_FORWARD_WITH_ENDORSEMENTS scenario.',
|
||||
async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
|
@ -285,7 +278,6 @@ describe('policy-vehicles.vue', () => {
|
|||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
// eslint-disable-next-line max-len
|
||||
expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(bailoutMessage.vehicleVinLookupError(vin, lookupReturnValue.data));
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
|
||||
navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
|
|
@ -296,9 +288,7 @@ describe('policy-vehicles.vue', () => {
|
|||
}
|
||||
);
|
||||
|
||||
test(
|
||||
// eslint-disable-next-line max-len
|
||||
'Vehicle not found in lookupVehicleByVin call => policyVinFound false and navigate forward with CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND scenario.',
|
||||
test('Vehicle not found in lookupVehicleByVin call => policyVinFound false and navigate forward with CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND scenario.',
|
||||
async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ export default {
|
|||
siteHeader,
|
||||
siteFooter,
|
||||
policyVehiclesQuestion,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
alert
|
||||
},
|
||||
|
|
|
|||
|
|
@ -88,9 +88,7 @@ export default {
|
|||
siteFooter,
|
||||
siteHeader,
|
||||
siteSubHeader,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
buttonQuestion,
|
||||
buttonMain,
|
||||
steeringModal,
|
||||
tpaRecalModal,
|
||||
|
|
@ -111,7 +109,7 @@ export default {
|
|||
vm.setCmsContent(resultMap.cmsContent);
|
||||
// open steering modal if it has body text, the state is defined in the CMS content and doesn't
|
||||
// populate if the state is not listed in the CMS content
|
||||
if (!!vm.$refs[STEERING_MODAL_REF_NAME].ModalBodyText) {
|
||||
if (vm.$refs[STEERING_MODAL_REF_NAME].ModalBodyText) {
|
||||
vm.openStateSteeringModal();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ export default {
|
|||
},
|
||||
|
||||
footerButtonClick() {
|
||||
if (this.tpaRecalAnswer && (!this.showAcknowledgementCheckbox || this.acknowledged )) {
|
||||
if (this.tpaRecalAnswer && (!this.showAcknowledgementCheckbox || this.acknowledged)) {
|
||||
this.$refs[this.ModalName]?.closeModal();
|
||||
this.$emit('buttonClick', this.tpaRecalAnswer);
|
||||
} else if (!this.tpaRecalAnswer) {
|
||||
|
|
@ -182,7 +182,6 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.form-test-error {
|
||||
p {
|
||||
font-weight: 500;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-env jest */
|
||||
import { render } from '@testing-library/vue';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import issPageValues from '@/router/router-constants/issPage-values';
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
<div class="fade-on-route-transition">
|
||||
<div class="justify-content-center">
|
||||
<siteHeader
|
||||
class="header"
|
||||
cmsWidgetName="SiteHeaderWidget" />
|
||||
</div>
|
||||
<div class="iss-heritage-container-width">
|
||||
|
|
@ -176,17 +175,13 @@ const getAvailableDates = async (
|
|||
storeAction.payload.endDate,
|
||||
storeAction.payload.shopAppointmentType,
|
||||
storeAction.payload.providerNumber
|
||||
).catch(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('Error fetching shop time slots...');
|
||||
});
|
||||
);
|
||||
} else if (storeAction.payload?.zipCodeOverride) {
|
||||
timeSlotsResponse = await useMainStore().getMobileTimeSlots(
|
||||
storeAction.payload.startDate,
|
||||
storeAction.payload.endDate,
|
||||
storeAction.payload.zipCodeOverride
|
||||
).catch(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('Error fetching mobile time slots...');
|
||||
});
|
||||
}
|
||||
|
|
@ -220,7 +215,6 @@ export default {
|
|||
datePicker,
|
||||
serviceLocation,
|
||||
siteFooter,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
},
|
||||
mixins: [BaseFormMixin],
|
||||
|
|
@ -346,7 +340,7 @@ export default {
|
|||
&& (((this.selectedAppointmentType === AppointmentTypeStrings.MOBILE
|
||||
|| this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP)
|
||||
&& serviceLocationToUse.mobileProviderNumber)
|
||||
|| (this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP
|
||||
|| ((this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP || this.selectedAppointmentType === AppointmentTypeStrings.DROP_OFF)
|
||||
&& (this.selectedProvider?.providerNumber || serviceLocationToUse.provider?.providerNumber)));
|
||||
|
||||
const damageInfo = useMainStore().order.damage.isRepair
|
||||
|
|
@ -655,8 +649,13 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
// Save service location then schedule and navigate forward
|
||||
await this.$refs.serviceLocation.forwardButtonAction();
|
||||
let appointmentTypeToUse = this.selectedAppointmentType;
|
||||
if (appointmentTypeToUse === AppointmentTypeStrings.IN_SHOP && this.selectedTimeSlotInfo.timeSlot?.isDropoff === true) {
|
||||
appointmentTypeToUse = AppointmentTypeStrings.DROP_OFF;
|
||||
}
|
||||
|
||||
// Save service location then save schedule then navigate forward
|
||||
await this.$refs.serviceLocation.forwardButtonAction(appointmentTypeToUse);
|
||||
this.mainStore.saveSchedule(this.selectedTimeSlotInfo.timeSlot);
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD,
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ import {
|
|||
getServiceabilityDetails
|
||||
} from '@/helpers/service-location-helper';
|
||||
import { deepClone } from '@/helpers/object-helper.js';
|
||||
// eslint-disable-next-line max-len
|
||||
import vehicleProtectedQuestion from '@/layouts/schedule-page/service-location/mobile-location-modal-question/vehicle-protected-question/vehicle-protected-question.vue';
|
||||
|
||||
export default {
|
||||
|
|
@ -195,7 +194,6 @@ export default {
|
|||
&& this.addressModel.zipCode !== ''
|
||||
&& this.internalModel.isVehicleProtected !== null
|
||||
) {
|
||||
// eslint-disable-next-line max-len
|
||||
return `${this.addressModel.streetAddress}\n${this.addressModel.city}, ${this.addressModel.state} ${this.addressModel.zipCode}`;
|
||||
}
|
||||
return this.getCmsContent(this.linkWidgetName, 'BodyText');
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ export default {
|
|||
buttonQuestion
|
||||
},
|
||||
props: {
|
||||
// eslint-disable-next-line vue/require-prop-types
|
||||
modelValue: {
|
||||
isVehicleProtected: Boolean
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-env jest */
|
||||
import baseMixin from '@/mixins/base-mixin';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ export default {
|
|||
}
|
||||
},
|
||||
selectedAppointmentType(newValue, oldValue) {
|
||||
if (this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP && this.selectedProvider && this.availabilityRating === null) {
|
||||
if (this.isInshop && this.selectedProvider && this.availabilityRating === null) {
|
||||
this.refreshAvailabilityRating();
|
||||
|
||||
const appointmenTypeServiceLocationObj = {
|
||||
|
|
@ -336,7 +336,7 @@ export default {
|
|||
},
|
||||
selectedProvider(newProvider, oldProvider) {
|
||||
if (newProvider?.providerNumber !== oldProvider?.providerNumber || this.availabilityRating === null) {
|
||||
const appointmentIsInshop = this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP;
|
||||
const appointmentIsInshop = this.isInshop;
|
||||
const returnedProvider = {
|
||||
provider: newProvider,
|
||||
refreshDatePicker: appointmentIsInshop && oldProvider?.providerNumber !== null
|
||||
|
|
@ -358,7 +358,7 @@ export default {
|
|||
await this.setData(initialData);
|
||||
this.initializingData = false;
|
||||
},
|
||||
async forwardButtonAction() {
|
||||
async forwardButtonAction(appointmentType) {
|
||||
let provider = this.selectedProvider;
|
||||
this.mainStore.updateMobileFee(null);
|
||||
if (this.isMobile) {
|
||||
|
|
@ -382,7 +382,7 @@ export default {
|
|||
state: this.state,
|
||||
zipCode: this.zipCode,
|
||||
zipCodeCtu: this.zipCodeCtu,
|
||||
appointmentType: this.selectedAppointmentType,
|
||||
appointmentType: appointmentType || this.selectedAppointmentType,
|
||||
provider
|
||||
});
|
||||
},
|
||||
|
|
@ -489,7 +489,6 @@ export default {
|
|||
this.setMobileProviderNumber(initialData.providers.mobileProviderNumber);
|
||||
let foundMatch = false;
|
||||
if (this.selectedProvider && this.selectedProvider.providerNumber) {
|
||||
// eslint-disable-next-line max-len
|
||||
const matchedProvider = initialData.providers.shopProviders.find((provider) => provider.providerNumber === this.selectedProvider.providerNumber);
|
||||
if (matchedProvider) {
|
||||
foundMatch = true;
|
||||
|
|
@ -497,7 +496,6 @@ export default {
|
|||
}
|
||||
}
|
||||
if (initialData.providers.shopProviders.length > 0 && !foundMatch) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
this.selectedProvider = initialData.providers.shopProviders[0];
|
||||
} else if (initialData.providers.shopProviders.length === 0) {
|
||||
this.selectedProvider = null;
|
||||
|
|
@ -563,7 +561,6 @@ export default {
|
|||
if (providers) {
|
||||
this.setMobileProviderNumber(providers.mobileProviderNumber);
|
||||
if (providers.shopProviders.length > 0) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
this.selectedProvider = providers.shopProviders[0];
|
||||
} else {
|
||||
this.selectedProvider = null;
|
||||
|
|
|
|||
|
|
@ -177,7 +177,6 @@ export default {
|
|||
return toTitleCase(this.modelValue.companyName);
|
||||
},
|
||||
modalHeaderText() {
|
||||
// eslint-disable-next-line max-len
|
||||
return this.getCmsContent(this.modalWidgetName, widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT).replaceAll('{custom:serviceZipcode}', this.internalZipcode);
|
||||
},
|
||||
modalFooterText() {
|
||||
|
|
@ -262,14 +261,12 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
// eslint-disable-next-line consistent-return
|
||||
async updateShops(autoExpand = false) {
|
||||
this.errorMessage = '';
|
||||
let result = await this.getNearbyShops(this.searchRadiusInMiles);
|
||||
let currentSearchIndex = this.searchRadiusArray.findIndex((option) => option.Name === this.searchRadiusInMiles);
|
||||
while (autoExpand && result.length === 0 && currentSearchIndex < this.searchRadiusArray.length - 1) {
|
||||
const newSearchRadius = this.searchRadiusArray[currentSearchIndex + 1].Name;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
result = await this.getNearbyShops(newSearchRadius);
|
||||
currentSearchIndex += 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,16 @@
|
|||
<buttonQuestion
|
||||
ref="buttonQuestion"
|
||||
v-model="selectedPackageName"
|
||||
class="service-package-question"
|
||||
:answers="servicePackageAnswers"
|
||||
:groupName="groupName"
|
||||
:questionText="questionText"
|
||||
:questionTextClasses="['service-package-question-text']"
|
||||
buttonTypeString="servicePackageRadio"
|
||||
:buttonTypeObject="servicePackageRadio"
|
||||
:validationRules="validationRules"
|
||||
:isRequired="isRequired" />
|
||||
:isRequired="isRequired"
|
||||
:isHorizontalLayout="true" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -17,9 +21,7 @@ import damageLocationsSelected from '@/constants/damage-locations-selected';
|
|||
import servicePackageRadio from '@/layouts/service-packages/service-package-question/service-package-radio/service-package-radio.vue';
|
||||
import partTypeStrings from '@/constants/part-type-strings';
|
||||
import { useMainStore } from '@/store';
|
||||
import allGlassPartsAndItemsHavePrices from '@/layouts/service-packages/service-package-helper/service-package-helper';
|
||||
import { getPriceOfLineItem } from '@/helpers/price-calculator';
|
||||
import { getHighestFullySatisfiedTier } from '@/helpers/service-package-helper.js';
|
||||
|
||||
const glassLocations = damageLocationsSelected;
|
||||
|
||||
|
|
@ -50,7 +52,8 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
servicePackageRadio,
|
||||
selectedPackageName: ''
|
||||
selectedPackageName: '',
|
||||
questionText: 'Select an option:'
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -58,7 +61,6 @@ export default {
|
|||
if (this.availableLineItems?.lineItems) {
|
||||
return this.availableLineItems.lineItems;
|
||||
}
|
||||
|
||||
return this.availableLineItems ?? [];
|
||||
},
|
||||
servicePackageAnswers() {
|
||||
|
|
@ -142,9 +144,6 @@ export default {
|
|||
}
|
||||
},
|
||||
watch: {
|
||||
availableLineItems() {
|
||||
this.selectDefaultPackage();
|
||||
},
|
||||
selectedPackageName(newValue) {
|
||||
const VapsProductsInSelectedPackage = this.getVapsLineItemsForSelectedPackage(newValue);
|
||||
this.$emit('vapsItemsSelected', VapsProductsInSelectedPackage);
|
||||
|
|
@ -213,17 +212,6 @@ export default {
|
|||
});
|
||||
return vapsPrice;
|
||||
},
|
||||
selectDefaultPackage() {
|
||||
const { glassToReplace, isRepair } = store.order.damage;
|
||||
const { vaps } = store.order.lineItems;
|
||||
const defaultTier = getHighestFullySatisfiedTier(
|
||||
glassToReplace ?? [],
|
||||
this.availableLineItems,
|
||||
isRepair,
|
||||
vaps ?? []
|
||||
);
|
||||
this.selectedPackageName = defaultTier;
|
||||
},
|
||||
getVapsLineItemsForSelectedPackage(packageName) {
|
||||
const vapsLineItemsForSelectedPackage = [];
|
||||
if (packageName === packageNames.TIER_TWO) {
|
||||
|
|
@ -280,3 +268,24 @@ export default {
|
|||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.service-package-question-text {
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
:deep(.package-main) {
|
||||
padding: 0 1.5rem 0 1.5rem;
|
||||
.package-wrapper:nth-child(2) {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
.package-wrapper:nth-child(3) {
|
||||
margin-right: 0;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -7,18 +7,24 @@
|
|||
:class="[buttonLabelSubCopy ? 'has-subheader' : '']"
|
||||
for="testradio">
|
||||
<div class="package-specs">
|
||||
<div>
|
||||
<p class="m-0">
|
||||
<span v-html="buttonLabel"></span>
|
||||
<div class="button-label">
|
||||
<div class="label-wrapper">
|
||||
<p class="m-0">
|
||||
<span
|
||||
class="mt-1"
|
||||
v-html="buttonLabel"></span>
|
||||
</p>
|
||||
<p
|
||||
v-if="buttonLabelSubCopy"
|
||||
class="sub-label m-0"
|
||||
v-html="buttonLabelSubCopy">
|
||||
</p>
|
||||
</div>
|
||||
<div class="pricing-info-mobile">
|
||||
<span
|
||||
class="pricing-info"
|
||||
class="price"
|
||||
v-html="buttonAuxiliaryCopy"></span>
|
||||
</p>
|
||||
<p
|
||||
v-if="buttonLabelSubCopy"
|
||||
class="sub-label m-0"
|
||||
v-html="buttonLabelSubCopy">
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hide-when-closed">
|
||||
<ul>
|
||||
|
|
@ -53,10 +59,16 @@
|
|||
<!--ms-n6-->
|
||||
<div
|
||||
v-if="buttonFooterCopy"
|
||||
class="package-footer fw-bold caption mt-4 ml-n4 mr-3"
|
||||
v-html="buttonFooterCopy"></div>
|
||||
class="package-footer fw-bold caption mt-4 mr-3"
|
||||
v-html="buttonFooterCopy">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pricing-info-desktop">
|
||||
<span
|
||||
class="price"
|
||||
v-html="buttonAuxiliaryCopy"></span>
|
||||
</div>
|
||||
</div>
|
||||
</baseInputButton>
|
||||
</template>
|
||||
|
|
@ -111,6 +123,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// TO DO: look into why the font renders differently than Heritage, even with the same specs
|
||||
.ml-n4 {
|
||||
margin-left: -$spacer * 2;
|
||||
}
|
||||
|
|
@ -121,8 +134,30 @@ export default {
|
|||
.package-wrapper {
|
||||
margin: 0.5rem 0;
|
||||
|
||||
.button-label {
|
||||
display: flex;
|
||||
&:before {
|
||||
content: "";
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin-right: 1rem;
|
||||
border-radius: 50%;
|
||||
border: 0.666667px solid #767676;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
min-width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.package-label {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
|
|
@ -132,14 +167,15 @@ export default {
|
|||
|
||||
+ .package-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
border: 1px solid $gray-300;
|
||||
box-shadow: 0px 4px 8px -4px rgba(0, 0, 0, 0.15), 0px 4px 24px -8px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 0.5rem;
|
||||
padding: 10px;
|
||||
border: 0.666667px solid #dddddd;
|
||||
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 0.25rem;
|
||||
overflow: hidden;
|
||||
min-height: 60px;
|
||||
max-height: 100px;
|
||||
|
|
@ -151,23 +187,11 @@ export default {
|
|||
min-height: 86px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin-right: 1rem;
|
||||
border-radius: 50%;
|
||||
border: 1px solid $gray-500;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 19px;
|
||||
top: 24px;
|
||||
left: 13px;
|
||||
top: 18px;
|
||||
border-radius: 50%;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
|
|
@ -176,9 +200,9 @@ export default {
|
|||
}
|
||||
|
||||
&:hover {
|
||||
+ .package-label {
|
||||
+ .button-label {
|
||||
&:before {
|
||||
border: 1px solid #8e9292;
|
||||
border: 0.666667px solid #767676;
|
||||
box-shadow: 0px 0px 0px 4px #9fcee6, 0px 1px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
|
@ -200,42 +224,37 @@ export default {
|
|||
}
|
||||
|
||||
+ .package-label {
|
||||
background-color: $blue-100;
|
||||
border: 1px solid $blue;
|
||||
background-color: $background-color-selected;
|
||||
border: 0.666667px solid $heritage-blue-secondary;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
+ .package-label {
|
||||
+ .button-label {
|
||||
&:before {
|
||||
box-shadow: 0px 0px 0px 1px $blue;
|
||||
box-shadow: 0px 0px 0px 1px $heritage-blue-primary;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
+ .package-label {
|
||||
&:after {
|
||||
background: $blue;
|
||||
background: $heritage-blue-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
+ .package-label {
|
||||
&:before {
|
||||
border: 2px solid $blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.package-footer {
|
||||
color: $red;
|
||||
font-weight: $font-weight-bold; // 600 in fmg
|
||||
margin-top: 0.5rem; // not in fmg
|
||||
color: #af2117;
|
||||
font-weight: $font-weight-bold;
|
||||
margin-top: 20px;
|
||||
font-size: 0.875rem;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.package-specs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 0;
|
||||
width: 100%;
|
||||
max-height: 0;
|
||||
transition: all 1s ease;
|
||||
|
|
@ -243,32 +262,28 @@ export default {
|
|||
max-height: 500px;
|
||||
}
|
||||
p {
|
||||
font-weight: $font-weight-bold; // 600 in fmg
|
||||
display: flex;
|
||||
color: #4d4e53;
|
||||
font-weight: 600;
|
||||
justify-content: space-between;
|
||||
|
||||
span {
|
||||
&.pricing-info {
|
||||
color: $green;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.sub-label {
|
||||
color: $green;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 1rem 0 0 -.6rem; // .9375rem in fmg
|
||||
margin: 0.5rem 0 0 1.25rem;
|
||||
padding: 0;
|
||||
color: $darker-gray;
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-bottom: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5rem;
|
||||
color: $darker-gray;
|
||||
|
||||
a {
|
||||
line-height: 1.5rem;
|
||||
|
|
@ -291,8 +306,41 @@ export default {
|
|||
.hide-when-closed {
|
||||
display: none;
|
||||
@include media-breakpoint-up(md) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pricing-info-desktop {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: flex-end;
|
||||
@include media-breakpoint-down(md) {
|
||||
display: none;
|
||||
}
|
||||
span {
|
||||
&.price {
|
||||
color: #075f35;
|
||||
font-size: 0.875rem;
|
||||
font-weight: $font-weight-bold;
|
||||
padding: 0.35rem 0 1rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pricing-info-mobile {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
margin-top: 0.25rem;
|
||||
@include media-breakpoint-up(md) {
|
||||
display: none;
|
||||
}
|
||||
span {
|
||||
&.price {
|
||||
color: #075f35;
|
||||
font-size: 0.875rem;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@
|
|||
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
||||
</div>
|
||||
<div class="iss-heritage-container-width">
|
||||
<div class="service-packages-container iss-heritage-content-container-width">
|
||||
<div class="service-packages-container">
|
||||
<siteSubHeader
|
||||
class="mt-4"
|
||||
subHeaderClasses="mt-5"
|
||||
justification="left"
|
||||
class="subheader mt-5"
|
||||
justification="center"
|
||||
cmsWidgetName="SiteSubHeaderWidget"
|
||||
issContainingPage="service-packages" />
|
||||
<servicePackageQuestion
|
||||
|
|
@ -25,15 +24,24 @@
|
|||
isRequired
|
||||
@vapsItemsSelected="vapsItemsSelectedAction"
|
||||
@link-event="openModalAction" />
|
||||
<p
|
||||
class="caption disclaimer"
|
||||
v-html="PriceDisclaimerText"></p>
|
||||
<div class="d-flex justify-content-center mt-8 mb-4">
|
||||
<buttonMain
|
||||
ref="buttonMain"
|
||||
class="continue-button"
|
||||
variant="navigation"
|
||||
:buttonText="'Continue'"
|
||||
@clickEvent="forwardButtonAction" />
|
||||
</div>
|
||||
<siteFooter
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
class="back-link"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@backClicked="navigateBack"
|
||||
@ForwardClicked="forwardButtonAction" />
|
||||
:isForwardButtonHidden="true"
|
||||
@backClicked="navigateBack" />
|
||||
</div>
|
||||
<p
|
||||
class="caption disclaimer"
|
||||
v-html="PriceDisclaimerText"></p>
|
||||
</div>
|
||||
<loadingModal
|
||||
ref="loadingModal"
|
||||
|
|
@ -72,6 +80,7 @@ import globalRules from '@/constants/global-rules';
|
|||
import servicePackageQuestion from '@/layouts/service-packages/service-package-question/service-package-question.vue';
|
||||
import issPageValues from '@/router/router-constants/issPage-values';
|
||||
import bailoutMessage from '@/constants/bailoutMessage';
|
||||
import buttonMain from '@/ux-components/button-main/button-main.vue';
|
||||
|
||||
const store = useMainStore();
|
||||
|
||||
|
|
@ -81,11 +90,11 @@ export default {
|
|||
siteHeader,
|
||||
siteFooter,
|
||||
siteSubHeader,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
servicePackageQuestion,
|
||||
loadingModal,
|
||||
contentGroupModal
|
||||
contentGroupModal,
|
||||
buttonMain
|
||||
},
|
||||
mixins: [baseFormMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
|
|
@ -210,30 +219,50 @@ export default {
|
|||
<style lang="scss" scoped>
|
||||
.iss-heritage-container-width {
|
||||
.service-packages-container {
|
||||
@include media-breakpoint-up(md) {
|
||||
width: 100%;
|
||||
.continue-button {
|
||||
width: 50%
|
||||
}
|
||||
}
|
||||
@include media-breakpoint-down(md) {
|
||||
.continue-button {
|
||||
width: 90%
|
||||
}
|
||||
}
|
||||
position: relative;
|
||||
min-height: 1px;
|
||||
padding-left: .9375rem;
|
||||
padding-right: .9375rem;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
:deep(.subheader-primary) {
|
||||
display: block;
|
||||
}
|
||||
:deep(.subheader-secondary) {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-top: 0.75rem;
|
||||
padding: 0px;
|
||||
p {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.subheader-secondary {
|
||||
margin-top: 0.5rem;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
margin: 0.5rem 0 1.5rem 0;
|
||||
|
||||
a {
|
||||
color: #4d5151;
|
||||
font-weight: 400;
|
||||
margin: 0.5rem 1rem 1.5rem 1.5rem;
|
||||
:deep(.external-text) {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.service-packages {
|
||||
:deep(strong) {
|
||||
font-weight: $font-weight-bold;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.back-link {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ export default {
|
|||
siteHeader,
|
||||
siteFooter,
|
||||
textBlock,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
},
|
||||
mixins: [BaseFormMixin],
|
||||
|
|
|
|||
|
|
@ -1,214 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TPA search page returns the initial data 1`] = `
|
||||
Object {
|
||||
"additionalButtonData": Object {
|
||||
"displayAvailabilityIndicators": false,
|
||||
},
|
||||
"dataLoaded": false,
|
||||
"filter": "",
|
||||
"mapZipCode": null,
|
||||
"providers": Array [],
|
||||
"reloadingProviders": false,
|
||||
"rules": Object {
|
||||
"filter": "option-required",
|
||||
"provider": "option-required",
|
||||
"zipCode": "zip-code-required|zip-code-search-format",
|
||||
},
|
||||
"selectedProviderNumber": "",
|
||||
"shopListButton": Object {
|
||||
"beforeMount": [Function],
|
||||
"components": Object {
|
||||
"baseInputButton": Object {
|
||||
"computed": Object {
|
||||
"buttonId": [Function],
|
||||
"eventTypes": [Function],
|
||||
"inputType": [Function],
|
||||
"isChecked": [Function],
|
||||
"isValueSelectedOnClick": [Function],
|
||||
},
|
||||
"data": [Function],
|
||||
"emits": Array [
|
||||
"update:modelValue",
|
||||
],
|
||||
"methods": Object {
|
||||
"handleBlur": [Function],
|
||||
"handleClick": [Function],
|
||||
"handleEventAction": [Function],
|
||||
"handleFocus": [Function],
|
||||
"handleSelectionChange": [Function],
|
||||
},
|
||||
"mounted": [Function],
|
||||
"name": "base-input-button",
|
||||
"props": Object {
|
||||
"buttonWrapperClasses": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"groupName": Object {
|
||||
"required": true,
|
||||
"type": [Function],
|
||||
},
|
||||
"inputClasses": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"isMultiSelect": [Function],
|
||||
"isRequired": Object {
|
||||
"default": true,
|
||||
"type": [Function],
|
||||
},
|
||||
"lastValuePushedToGa": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"modelValue": Object {
|
||||
"required": true,
|
||||
"validator": [Function],
|
||||
},
|
||||
"selectingInitiatesLoad": Object {
|
||||
"default": false,
|
||||
"type": [Function],
|
||||
},
|
||||
"setLastValuePushedToGa": [Function],
|
||||
"suppressError": [Function],
|
||||
"validationRules": Object {
|
||||
"default": "",
|
||||
"type": [Function],
|
||||
},
|
||||
"value": Object {
|
||||
"required": true,
|
||||
"type": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
},
|
||||
"valueToLogType": [Function],
|
||||
},
|
||||
"render": [Function],
|
||||
"setup": [Function],
|
||||
},
|
||||
"loader": Object {
|
||||
"computed": Object {
|
||||
"cssProps": [Function],
|
||||
},
|
||||
"methods": Object {
|
||||
"captureClick": [Function],
|
||||
},
|
||||
"name": "loader",
|
||||
"props": Object {
|
||||
"allowPageInteraction": Object {
|
||||
"default": false
|
||||
"type": [Function],
|
||||
},
|
||||
"height": Object {
|
||||
"default": 1,
|
||||
"type": [Function],
|
||||
},
|
||||
"loaderColor": Object {
|
||||
"type": [Function],
|
||||
},
|
||||
"loaderPosition": Object {
|
||||
"type": [Function],
|
||||
},
|
||||
"width": Object {
|
||||
"default": 1,
|
||||
"type": [Function],
|
||||
},
|
||||
},
|
||||
"render": [Function],
|
||||
},
|
||||
},
|
||||
"computed": Object {
|
||||
"availabilityRatingClass": [Function],
|
||||
"badgeText": [Function],
|
||||
"isLoaderDisplayed": [Function],
|
||||
},
|
||||
"data": [Function],
|
||||
"mixins": Array [
|
||||
Object {
|
||||
"computed": Object {
|
||||
"selectedValue": Object {
|
||||
"get": [Function],
|
||||
"set": [Function],
|
||||
},
|
||||
},
|
||||
"model": Object {
|
||||
"event": "change",
|
||||
"prop": "modelValue",
|
||||
},
|
||||
"props": Object {
|
||||
"additionalButtonData": [Function],
|
||||
"additionalButtonStyling": [Function],
|
||||
"altText": Object {
|
||||
"default": "",
|
||||
"type": [Function],
|
||||
},
|
||||
"buttonAuxiliaryCopy": [Function],
|
||||
"buttonBodyCopy": [Function],
|
||||
"buttonFooterCopy": [Function],
|
||||
"buttonImage": [Function],
|
||||
"buttonImageId": [Function],
|
||||
"buttonLabel": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"buttonLabelSubCopy": [Function],
|
||||
"groupName": Object {
|
||||
"required": true,
|
||||
"type": [Function],
|
||||
},
|
||||
"isMultiSelect": [Function],
|
||||
"isRequired": Object {
|
||||
"default": true,
|
||||
"type": [Function],
|
||||
},
|
||||
"isWide": [Function],
|
||||
"lastValuePushedToGa": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"modelValue": Object {
|
||||
"required": true,
|
||||
"validator": [Function],
|
||||
},
|
||||
"screenReaderOnlyText": [Function],
|
||||
"selectingInitiatesLoad": Object {
|
||||
"default": false,
|
||||
"type": [Function],
|
||||
},
|
||||
"setLastValuePushedToGa": [Function],
|
||||
"suppressError": [Function],
|
||||
"textPosition": [Function],
|
||||
"validationRules": Object {
|
||||
"default": "",
|
||||
"type": [Function],
|
||||
},
|
||||
"value": Object {
|
||||
"required": true,
|
||||
"type": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
},
|
||||
"valueToLogType": [Function],
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "shop-list-button",
|
||||
"render": [Function],
|
||||
},
|
||||
"widget": Object {
|
||||
"filterByQuestion": "FilterByQuestion",
|
||||
"noNetworkShopsAlert": "NoNetworkShopsAlertWidget",
|
||||
"searchInstructions": "SearchInstructions",
|
||||
"shopNotListedLink": "ShopNotListedLink",
|
||||
"siteFooter": "SiteFooterWidget",
|
||||
"siteHeader": "SiteHeaderWidget",
|
||||
"tpaSearchQuestion": "TPASearchQuestion",
|
||||
},
|
||||
"zipCode": null,
|
||||
}
|
||||
`;
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -15,26 +15,26 @@
|
|||
<label
|
||||
id="tpaSearchQuestionLabel"
|
||||
for="tpaSearchQuestionField"
|
||||
class="text-center fs-5 mt-5 mb-0 text-black w-100">
|
||||
class="fs-5 mt-5 text-black w-100">
|
||||
{{ tpaSearchQuestionLabel }}
|
||||
</label>
|
||||
<label
|
||||
id="searchInstructions"
|
||||
for="tpaSearchQuestionField"
|
||||
class="text-center small darker-gray w-100 mb-4">
|
||||
class="darker-gray w-100 mb-4">
|
||||
{{ searchInstructionsText }}
|
||||
</label>
|
||||
<div class="mb-5">
|
||||
<textboxQuestion
|
||||
id="tpaSearchQuestionField"
|
||||
v-model="zipCode"
|
||||
v-model="tpaSearchValue"
|
||||
inputId="tpaSearchQuestionFieldInput"
|
||||
:cmsWidgetName="widget.tpaSearchQuestion"
|
||||
:includeSearchIcon="true"
|
||||
:displayQuestionText="false"
|
||||
isRequired
|
||||
:isDisabled="reloadingProviders"
|
||||
:validationRules="rules.zipCode"
|
||||
:validationRules="rules.tpaSearch"
|
||||
@clickEvent="searchClick" />
|
||||
</div>
|
||||
</Form>
|
||||
|
|
@ -52,10 +52,10 @@
|
|||
@invalidSubmit="onInvalidSubmit">
|
||||
<dropdownQuestion
|
||||
id="searchRadiusFilter"
|
||||
v-model="filter"
|
||||
v-model="radiusFilter"
|
||||
:cmsWidgetName="widget.filterByQuestion"
|
||||
inputId="searchRadiusFilterInput"
|
||||
:options="filterOptions"
|
||||
:options="radiusFilterOptions"
|
||||
disableAutoFill
|
||||
:isDisabled="reloadingProviders"
|
||||
:validationRules="rules.filter" />
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
id="selectProviderQuestion"
|
||||
v-model="selectedProviderNumber"
|
||||
buttonTypeString="shopListButton"
|
||||
:buttonTypeObject="shopListButton"
|
||||
:buttonTypeObject="tpaShopListButton"
|
||||
class="radioQuestion"
|
||||
:answers="providerButtonData"
|
||||
groupName="chooseShop"
|
||||
|
|
@ -99,12 +99,23 @@
|
|||
:text="shopNotListedModalLink"
|
||||
@clickEvent="doNotSeeMyShopLinkClick" />
|
||||
</div>
|
||||
<hr>
|
||||
<siteFooter
|
||||
class="mt-6"
|
||||
ref="siteFooter"
|
||||
:cmsWidgetName="widget.siteFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
@backClicked="navigateBack" />
|
||||
<div class="need-help-link-container">
|
||||
<textLink
|
||||
id="needHelpLink"
|
||||
v-if="needHelpLinkText"
|
||||
linkType="navigation"
|
||||
:text="needHelpLinkText"
|
||||
href="#"
|
||||
@clickEvent="needHelpLinkClick" />
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
|
|
@ -123,7 +134,7 @@ import textLink from '@/ux-components/text-link/text-link.vue';
|
|||
import alert from '@/ux-components/alert/alert.vue';
|
||||
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
||||
import googleMap from '@/iss-components/google-map/google-map.vue';
|
||||
import shopListButton from '@/iss-components/shop-list-button/shop-list-button.vue';
|
||||
import tpaShopListButton from '@/iss-components/tpa-shop-list-button/tpa-shop-list-button.vue';
|
||||
import loader from '@/ux-components/loader/loader.vue';
|
||||
|
||||
// Supporting files
|
||||
|
|
@ -135,53 +146,8 @@ import widgetFields from '@/constants/cms-widget-fields.js';
|
|||
import { shallowRef } from 'vue';
|
||||
import { toTitleCase, toDisplayPhoneNumber } from '@/helpers/text-helper.js';
|
||||
import bailoutMessage from '@/constants/bailoutMessage';
|
||||
|
||||
const radiusFilterPairs = [
|
||||
{ radius: 15, filter: '15 miles' },
|
||||
{ radius: 25, filter: '25 miles' },
|
||||
{ radius: 50, filter: '50 miles' },
|
||||
{ radius: 100, filter: '100 miles' }
|
||||
];
|
||||
|
||||
function convertRadiusFilterToInteger(filter) {
|
||||
const pair = radiusFilterPairs.find((p) => p.filter === filter);
|
||||
return pair?.radius ?? 0;
|
||||
}
|
||||
|
||||
async function getInitialSearchData() {
|
||||
const customerZipCode = useMainStore().order.customer.address.zipCode;
|
||||
const serviceLocationZipCode = useMainStore().order.serviceLocation.zipCode;
|
||||
const zipCode = serviceLocationZipCode ?? customerZipCode;
|
||||
|
||||
const { searchFilter } = useMainStore().order.serviceLocation;
|
||||
let { providerNumber } = useMainStore().order.serviceLocation.provider;
|
||||
|
||||
let providers = [];
|
||||
let radius = 0;
|
||||
if (searchFilter) {
|
||||
radius = convertRadiusFilterToInteger(searchFilter);
|
||||
const result = await useMainStore().getTpaProviders(zipCode, radius);
|
||||
providers = result?.data?.shopProviders ?? [];
|
||||
|
||||
return { zipCode, filter: searchFilter, providers, providerNumber };
|
||||
}
|
||||
|
||||
providerNumber = null;
|
||||
let pairIndex = 0;
|
||||
let filter = '';
|
||||
while (providers.length === 0 && pairIndex < radiusFilterPairs.length) {
|
||||
const pair = radiusFilterPairs[pairIndex];
|
||||
radius = pair.radius;
|
||||
filter = pair.filter;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const result = await useMainStore().getTpaProviders(zipCode, radius);
|
||||
providers = result?.data?.shopProviders ?? [];
|
||||
|
||||
pairIndex += 1;
|
||||
}
|
||||
|
||||
return { zipCode, filter, providers, providerNumber };
|
||||
}
|
||||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
import issPageValues from '@/router/router-constants/issPage-values';
|
||||
|
||||
export default {
|
||||
name: 'tpa-search',
|
||||
|
|
@ -195,28 +161,44 @@ export default {
|
|||
loader,
|
||||
googleMap,
|
||||
siteFooter,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
},
|
||||
mixins: [BaseFormMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
const { zipCode, filter, providers, providerNumber } =
|
||||
await getInitialSearchData();
|
||||
const cmsContent = await fetchCmsContentForPage(to.query.issPage);
|
||||
|
||||
const zipCode = useMainStore().order.customer.address.zipCode;
|
||||
const pageData = useMainStore().pageData(to.query.issPage);
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
const providersPromise = useMainStore().getTpaAndSafeliteProviders(zipCode, pageData?.tpaSearchValue ?? '');
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: 'cmsContent',
|
||||
promise: cmsContentPromise
|
||||
},
|
||||
{
|
||||
resultKey: 'providers',
|
||||
promise: providersPromise
|
||||
}
|
||||
];
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
next(async (vm) => {
|
||||
vm.setCmsContent(cmsContent);
|
||||
vm.setInitialSearchData(zipCode, filter, providers, providerNumber);
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.mapZipCode = zipCode;
|
||||
vm.providers = resultMap.providers;
|
||||
if (pageData) {
|
||||
vm.tpaSearchValue = pageData?.tpaSearchValue ?? vm.mapZipCode;
|
||||
vm.radiusFilter = pageData?.radiusFilter ?? 25;
|
||||
}
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataLoaded: false,
|
||||
zipCode: null,
|
||||
pageData: null,
|
||||
tpaSearchValue: null,
|
||||
mapZipCode: null,
|
||||
filter: '',
|
||||
radiusFilter: null,
|
||||
providers: [],
|
||||
selectedProviderNumber: '',
|
||||
selectedProviderNumber: useMainStore().order.serviceLocation.provider.providerNumber,
|
||||
reloadingProviders: false,
|
||||
additionalButtonData: {
|
||||
displayAvailabilityIndicators: false
|
||||
|
|
@ -228,28 +210,28 @@ export default {
|
|||
filterByQuestion: 'FilterByQuestion',
|
||||
noNetworkShopsAlert: 'NoNetworkShopsAlertWidget',
|
||||
shopNotListedLink: 'ShopNotListedLink',
|
||||
siteFooter: 'SiteFooterWidget'
|
||||
siteFooter: 'SiteFooterWidget',
|
||||
needHelpLink: 'NeedHelpLink'
|
||||
},
|
||||
rules: {
|
||||
zipCode: `${globalRules.ZIP_CODE_REQUIRED}|${globalRules.ZIP_CODE_SEARCH_FORMAT}`,
|
||||
tpaSearch: globalRules.TPA_SEARCH_FORMAT,
|
||||
filter: globalRules.OPTION_REQUIRED, // TODO do we even need this?
|
||||
provider: globalRules.OPTION_REQUIRED
|
||||
},
|
||||
shopListButton: shallowRef(shopListButton)
|
||||
tpaShopListButton: shallowRef(tpaShopListButton)
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filterOptions() {
|
||||
const filterByAnswers =
|
||||
this.getCmsContent(
|
||||
radiusFilterOptions() {
|
||||
const radiusFilterOptions = this.getCmsContent(
|
||||
this.widget.filterByQuestion,
|
||||
widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
|
||||
) ?? [];
|
||||
const filterByAnswersObj = {};
|
||||
[...filterByAnswers].forEach((answer) => {
|
||||
filterByAnswersObj[answer.Name] = answer.Name;
|
||||
});
|
||||
return filterByAnswersObj;
|
||||
const options = {};
|
||||
for (const option of radiusFilterOptions) {
|
||||
options[option.Name] = option.Text;
|
||||
}
|
||||
return options;
|
||||
},
|
||||
tpaSearchQuestionLabel() {
|
||||
return this.getCmsContent(
|
||||
|
|
@ -257,6 +239,9 @@ export default {
|
|||
widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT
|
||||
);
|
||||
},
|
||||
needHelpLinkText() {
|
||||
return this.getCmsContent(this.widget.needHelpLink, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
},
|
||||
searchInstructionsText() {
|
||||
return this.getCmsContent(
|
||||
this.widget.searchInstructions,
|
||||
|
|
@ -269,25 +254,25 @@ export default {
|
|||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
},
|
||||
radiusInMiles() {
|
||||
return convertRadiusFilterToInteger(this.filter);
|
||||
},
|
||||
noNetworkShopsAlertHeaderText() {
|
||||
return this.getCmsContent(
|
||||
this.widget.noNetworkShopsAlert,
|
||||
widgetFields.ALERT_WIDGET.HEADLINE_TEXT
|
||||
)?.replaceAll('{custom:radiusInMiles}', this.radiusInMiles);
|
||||
)?.replaceAll('{custom:radiusInMiles}', this.radiusFilter + " miles");
|
||||
},
|
||||
radiusFilteredAndCappedProviders() {
|
||||
return this.providers.filter((provider) => provider.distanceInMiles <= this.radiusFilter).slice(0, 5);
|
||||
},
|
||||
providerAddresses() {
|
||||
return this.providers?.map((provider) => ({
|
||||
title: provider.companyName,
|
||||
return this.radiusFilteredAndCappedProviders?.map((provider) => ({
|
||||
title: provider.companyName.toLowerCase(),
|
||||
fullAddress: this.getFullProviderAddress(provider),
|
||||
addressLines: [this.getProviderAddress(provider), this.getProviderCityZipState(provider)]
|
||||
})) ?? [];
|
||||
},
|
||||
providerButtonData() {
|
||||
return (
|
||||
this.providers?.map((provider) =>
|
||||
this.radiusFilteredAndCappedProviders?.map((provider) =>
|
||||
this.getShopButtonDataFromProvider(provider)) ?? []
|
||||
);
|
||||
},
|
||||
|
|
@ -298,14 +283,6 @@ export default {
|
|||
}
|
||||
},
|
||||
watch: {
|
||||
async filter() {
|
||||
if (this.dataLoaded) {
|
||||
await this.getProviderButtonData().then((providers) => {
|
||||
this.providers = providers;
|
||||
});
|
||||
this.mapZipCode = this.zipCode;
|
||||
}
|
||||
},
|
||||
providers(newProviders) {
|
||||
if (this.dataLoaded) {
|
||||
this.selectedProviderNumber =
|
||||
|
|
@ -318,7 +295,6 @@ export default {
|
|||
const provider = this.providers?.find((p) => p.providerNumber === newNumber);
|
||||
if (provider && this.dataLoaded) {
|
||||
useMainStore().updateServiceLocation({
|
||||
searchFilter: this.filter,
|
||||
zipCode: this.zipCode,
|
||||
provider: {
|
||||
providerNumber: provider?.providerNumber,
|
||||
|
|
@ -342,13 +318,6 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
setInitialSearchData(zipCode, filter, providers, providerNumber) {
|
||||
this.zipCode = zipCode;
|
||||
this.mapZipCode = zipCode;
|
||||
this.filter = filter;
|
||||
this.providers = providers;
|
||||
this.selectedProviderNumber = providerNumber;
|
||||
},
|
||||
getFullProviderAddress(provider) {
|
||||
const addressLine1 = this.getProviderAddress(provider);
|
||||
const addressLine2 = this.getProviderCityZipState(provider);
|
||||
|
|
@ -379,16 +348,16 @@ export default {
|
|||
}
|
||||
return addressLine2;
|
||||
},
|
||||
async getProviderButtonData() {
|
||||
async getProviderButtonData(shopName = "") {
|
||||
this.reloadingProviders = true;
|
||||
const getTpaProvidersResult = await useMainStore().getTpaProviders(
|
||||
this.zipCode,
|
||||
this.radiusInMiles
|
||||
const getTpaProvidersResult = await useMainStore().getTpaAndSafeliteProviders(
|
||||
this.mapZipCode,
|
||||
shopName
|
||||
);
|
||||
this.reloadingProviders = false;
|
||||
return getTpaProvidersResult?.data?.shopProviders ?? [];
|
||||
return getTpaProvidersResult?.data ?? [];
|
||||
},
|
||||
doNotSeeMyShopLinkClick() {
|
||||
needHelpLinkClick() {
|
||||
this.mainStore.setBailout(bailoutMessage.RequestCallback());
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_NEED_HELP,
|
||||
|
|
@ -396,8 +365,12 @@ export default {
|
|||
);
|
||||
},
|
||||
async searchClick() {
|
||||
this.providers = await this.getProviderButtonData();
|
||||
this.mapZipCode = this.zipCode;
|
||||
if (isNaN(this.tpaSearchValue)) {
|
||||
this.providers = await this.getProviderButtonData(this.tpaSearchValue);
|
||||
} else {
|
||||
this.mapZipCode = this.tpaSearchValue;
|
||||
this.providers = await this.getProviderButtonData();
|
||||
}
|
||||
},
|
||||
forwardButtonAction() {
|
||||
if (this.selectedProviderIsSafeliteShop) {
|
||||
|
|
@ -407,16 +380,12 @@ export default {
|
|||
? this.navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP
|
||||
: this.navigationScenarios
|
||||
.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP;
|
||||
// Do not save the zip code as the tpa search value, prevents an outdated zipcode from being used
|
||||
// as the default search value if customer goes back and changes zip earlier in the flow
|
||||
const tpaSearchValueToSave = this.tpaSearchValue === this.mapZipCode ? '' : this.tpaSearchValue;
|
||||
this.savePageDataToStore(issPageValues.TPA_SEARCH, {tpaSearchValue: tpaSearchValueToSave, radiusFilter: this.radiusFilter});
|
||||
this.$router.navigate(scenario, this.$route);
|
||||
},
|
||||
getCustomValueFromString(str) {
|
||||
switch (str) {
|
||||
case 'radiusInMiles':
|
||||
return this.radiusInMiles;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getShopButtonDataFromProvider(provider) {
|
||||
const cellNumber = toDisplayPhoneNumber(provider?.phoneNumber);
|
||||
const distance =
|
||||
|
|
@ -426,7 +395,7 @@ export default {
|
|||
: null;
|
||||
|
||||
return {
|
||||
buttonLabel: provider?.companyName ?? '',
|
||||
buttonLabel: provider?.companyName.toLowerCase() ?? '',
|
||||
buttonLabelSubCopy: distance === null ? '' : `${distance} mi`,
|
||||
buttonBodyCopy: `${this.getFullProviderAddress(provider)}<br>${
|
||||
cellNumber ?? ''
|
||||
|
|
@ -442,4 +411,30 @@ export default {
|
|||
.darker-gray {
|
||||
color: map-get($colors, 'darker-gray');
|
||||
}
|
||||
|
||||
#tpaSearchQuestionLabel {
|
||||
margin-bottom: .625rem;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 27.1875rem;
|
||||
}
|
||||
|
||||
#providerSelectionForm {
|
||||
#alertNoNetworkProviders {
|
||||
:deep(.alert) {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1.375rem 0;
|
||||
}
|
||||
|
||||
.need-help-link-container {
|
||||
margin-top:1.875rem;
|
||||
margin-bottom: 2.1875rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -131,12 +131,10 @@ export default {
|
|||
components: {
|
||||
siteHeader,
|
||||
textBlock,
|
||||
buttonMain,
|
||||
reviewBlock,
|
||||
deductibleBox,
|
||||
siteFooter,
|
||||
contactDetailsDrawer,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
alert,
|
||||
modal
|
||||
|
|
@ -300,7 +298,6 @@ export default {
|
|||
this.getEditShopLinkText,
|
||||
() => this.navigate(this.navigationScenarios.EDIT_PREFERRED_SHOP)
|
||||
),
|
||||
// eslint-disable-next-line max-len
|
||||
this.getSection(
|
||||
this.getContactInfoTitle,
|
||||
this.getContactInfoLines,
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ export default {
|
|||
watch: {
|
||||
isAvailable(val) {
|
||||
// CHECK TO UPDATE SELECTED VALUES WHEN ISAVAILABLE IS TRUE
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
val && this.updateSelectedValues();
|
||||
},
|
||||
shouldDisplayReplaceOptionsQuestion(shouldDisplayReplaceOptionsQuestion) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-env jest */
|
||||
import { mount, flushPromises } from '@vue/test-utils';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import navigationScenarios from '@/router/router-constants/navigation-scenarios';
|
||||
|
|
@ -6,6 +5,7 @@ import routerParams from '@/router/router-constants/router-params';
|
|||
import { useMainStore } from '@/store';
|
||||
import vehicleCategories from '@/constants/vehicle-categories';
|
||||
import VehicleDamageComponent from '@/layouts/vehicle-damage/vehicle-damage.vue';
|
||||
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
|
||||
|
||||
const mockRoute = {
|
||||
params: {}
|
||||
|
|
@ -13,6 +13,16 @@ const mockRoute = {
|
|||
const mockRouter = {
|
||||
navigate: jest.fn()
|
||||
};
|
||||
jest.mock('@/mixins/vehicle-questions-mixin', () => ({
|
||||
methods: {
|
||||
navigateForward: jest.fn(),
|
||||
getPartsOrQuestions: jest.fn(() => Promise.resolve({
|
||||
data: {
|
||||
partsOrQuestions: []
|
||||
}
|
||||
}))
|
||||
}
|
||||
}));
|
||||
const mountOptions = {
|
||||
global: {
|
||||
mixins: [
|
||||
|
|
@ -92,6 +102,43 @@ describe('vehicle-damage.vue', () => {
|
|||
expect(mockRouter.navigate)
|
||||
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_REPAIR, mockRoute);
|
||||
});
|
||||
test('When damage selected is a replace but not windshield, navigate forward from vehicle-questions-mixin', async () => {
|
||||
mountOptions.global.plugins = [createTestingPinia({
|
||||
initialState: {
|
||||
main: {
|
||||
order: {
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [{ glassLocation: 'Rear', glassName: 'Stationary' }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})];
|
||||
const wrapper = mount(VehicleDamageComponent, mountOptions);
|
||||
const siteFooterWrapper = wrapper.getComponent({ ref: 'siteFooter' });
|
||||
|
||||
useMainStore().getSupportingItems = jest.fn().mockImplementation(() => Promise.resolve({
|
||||
data: { data: [
|
||||
{
|
||||
description: null,
|
||||
partNumber: 'SUPPLIES-REPAIR',
|
||||
partType: 'REPAIR FEE'
|
||||
},
|
||||
{
|
||||
description: null,
|
||||
partNumber: 'WSREPAIR',
|
||||
partType: 'REPAIR FEE'
|
||||
}
|
||||
] }
|
||||
}));
|
||||
|
||||
siteFooterWrapper.vm.$emit('forwardClicked');
|
||||
|
||||
await flushPromises();
|
||||
expect(vehicleQuestionsMixin.methods.getPartsOrQuestions).toHaveBeenCalledTimes(1);
|
||||
expect(vehicleQuestionsMixin.methods.navigateForward).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
test('Error in getPartsOrQuestions call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario', async () => {
|
||||
mountOptions.global.plugins = [createTestingPinia({
|
||||
initialState: {
|
||||
|
|
@ -116,7 +163,7 @@ describe('vehicle-damage.vue', () => {
|
|||
const partsQuestionsErrorResponse = {
|
||||
error: 'Error getting parts'
|
||||
};
|
||||
useMainStore().getPartsOrQuestions = jest.fn().mockImplementation(() => (
|
||||
vehicleQuestionsMixin.methods.getPartsOrQuestions.mockImplementation(() => (
|
||||
partsQuestionsErrorResponse
|
||||
));
|
||||
siteFooterWrapper.vm.$emit('forwardClicked');
|
||||
|
|
|
|||
|
|
@ -132,7 +132,6 @@ export default {
|
|||
damageLocationQuestion,
|
||||
windshieldOptions,
|
||||
replaceOptionsQuestion,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
alert
|
||||
},
|
||||
|
|
@ -217,6 +216,13 @@ export default {
|
|||
=== damageLocationsSelected.REPAIR
|
||||
);
|
||||
},
|
||||
isWindshieldReplace() {
|
||||
return (
|
||||
this.isWindshieldDamageLocation
|
||||
&& this.selectedWindshieldOptions.selectedWindshieldDamageType
|
||||
=== damageLocationsSelected.REPLACE
|
||||
);
|
||||
},
|
||||
isDriverSideReplace() {
|
||||
if (!this.isSideDoorDamageLocation) return false;
|
||||
|
||||
|
|
@ -419,8 +425,8 @@ export default {
|
|||
this.navigationScenarios.CLICKED_FORWARD_WITH_REPAIR,
|
||||
this.$route
|
||||
);
|
||||
} else if (this.mainStore.order.vehicle.vin) {
|
||||
// If vin already exists, navigate directly to vin-lookup
|
||||
} else if (this.mainStore.order.vehicle.vin || !this.isWindshieldReplace) {
|
||||
// If vin already exists or not replacing windshield, get parts/questions and navigate forward
|
||||
|
||||
const partsOrQuestionsResponse = await this.getPartsOrQuestions();
|
||||
if (partsOrQuestionsResponse.error) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
// eslint-disable-next-line max-len
|
||||
import windshieldChipCountQuestion from '@/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
// eslint-disable-next-line max-len
|
||||
import windshieldDamageTypeQuestion from '@/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-env jest */
|
||||
import { mount } from '@vue/test-utils';
|
||||
import baseMixin from '@/mixins/base-mixin';
|
||||
import navigationScenarios from '@/router/router-constants/navigation-scenarios';
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ import TextBlock from '@/digital-components/text-block/text-block.vue';
|
|||
export default {
|
||||
name: 'vehicle-lookup',
|
||||
components: {
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
SiteFooter,
|
||||
siteHeader,
|
||||
|
|
|
|||
|
|
@ -41,13 +41,14 @@ function setupMocks({ glassNameProp, glassLocationProp, colorAnswersProp, modelV
|
|||
|
||||
};
|
||||
|
||||
const wrapper = shallowMount(glassPartQuestion, mountOptions);
|
||||
|
||||
// Mock store
|
||||
const partsOrQuestions = pageData ?? { partsOrQuestions: [{ glassName: 'Stationary', glassLocation: 'Rear', parts: [] }] };
|
||||
useMainStore().pageData = jest.fn();
|
||||
useMainStore().pageData.mockReturnValue(partsOrQuestions);
|
||||
document.querySelector = jest.fn().mockReturnValue({ clicked: false, click: jest.fn() });
|
||||
|
||||
const wrapper = shallowMount(glassPartQuestion, mountOptions);
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
|
|
@ -62,36 +63,23 @@ describe('glass-part-question.vue', () => {
|
|||
window.console.log(wrapper.vm.featureListData['Green Tint'][0].Text);
|
||||
// Assert
|
||||
expect(Object.keys(wrapper.vm.featureListData).length).toBe(2);
|
||||
expect(wrapper.vm.featureListData['Green Tint'][0].Text).toBe('heated glass, solar, 1 hole');
|
||||
expect(wrapper.vm.featureListData['Green Tint'][0].Text).toBe('Heated glass, Solar, 1 Hole');
|
||||
expect(wrapper.vm.featureListData['Green Tint'][0].Name).toBe('DB12209GTYN');
|
||||
expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Text).toBe('heated glass, solar, 1 hole');
|
||||
expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Text).toBe('Heated glass, Solar, 1 Hole');
|
||||
expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Name).toBe('DB12209YPYN');
|
||||
});
|
||||
|
||||
test('Tint mapper, should get tint image by glassLocation and tintColor', async () => {
|
||||
test('Tint mapper, should get return css class based on tint color', async () => {
|
||||
// Arrange
|
||||
|
||||
const { wrapper } = setupMocks(featureListData);
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
const tintSourceImage = wrapper.vm.getTintSourceImage('Rear', 'Green Tint');
|
||||
const tintClass = wrapper.vm.convertTintToCSSClass('Green Tint');
|
||||
|
||||
// Assert
|
||||
expect(tintSourceImage).toBe('Glass-NoShade-GreenTint.svg');
|
||||
});
|
||||
|
||||
test('Tint mapper, should return empty string if no tint map found', async () => {
|
||||
// Arrange
|
||||
|
||||
const { wrapper } = setupMocks(featureListData);
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
const tintSourceImage = wrapper.vm.getTintSourceImage('Rear', 'Crazy Rainbow Tint');
|
||||
|
||||
// Assert
|
||||
expect(tintSourceImage).toBe('');
|
||||
expect(tintClass).toBe('green-tint');
|
||||
});
|
||||
|
||||
test('Should emit updateModelValue, and have correct attributes', async () => {
|
||||
|
|
@ -122,7 +110,7 @@ describe('glass-part-question.vue', () => {
|
|||
{ partNumber: 'DB12209GTYN', color: 'Green Tint' }
|
||||
]);
|
||||
expect(listCard.attributes('groupname')).toBe('Rear-Stationary');
|
||||
expect(listCard.attributes('validationrules')).toBe('Rear-Stationary-tint-required');
|
||||
expect(listCard.attributes('validationrules')).toBe('Rear-Stationary-part-required');
|
||||
});
|
||||
|
||||
test('default is selected if only one option', async () => {
|
||||
|
|
@ -136,58 +124,42 @@ describe('glass-part-question.vue', () => {
|
|||
}
|
||||
]
|
||||
};
|
||||
const updateSpy = jest.spyOn(glassPartQuestion.methods, 'updateSelectedPartNumber');
|
||||
const { wrapper } = setupMocks(featureListData);
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.setData({ selectedTint: 'Green Tint' });
|
||||
// take emitted value, pass down as modelValue
|
||||
// yes, yes, it's not ideal
|
||||
await wrapper.setProps({ modelValue: wrapper.emitted()['update:modelValue'][0][0] });
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.selectedPartNumber).toBe('DB12209GTYN');
|
||||
expect(updateSpy).toHaveBeenCalledWith('DB12209GTYN');
|
||||
});
|
||||
|
||||
const partsForSelectedTintTestCases = [
|
||||
const partsForLocationTestCases = [
|
||||
[
|
||||
'Rear',
|
||||
'Stationary',
|
||||
'Green Tint',
|
||||
[
|
||||
{ partNumber: 'Glass1', color: 'Green Tint' },
|
||||
{ partNumber: 'Glass2', color: 'Blue Tint' },
|
||||
{ partNumber: 'Glass3', color: 'Green Tint' },
|
||||
{ partNumber: 'Glass4', color: 'Green Tint' },
|
||||
{ partNumber: 'Glass6', color: 'Green Tint' }
|
||||
{ partNumber: 'Glass5', color: 'Blue Tint' },
|
||||
{ partNumber: 'Glass6', color: 'Green Tint' },
|
||||
{ partNumber: 'Glass7', color: 'Red Tint' }
|
||||
]
|
||||
],
|
||||
[
|
||||
'Rear',
|
||||
'Stationary',
|
||||
'Blue Tint',
|
||||
[
|
||||
{ partNumber: 'Glass2', color: 'Blue Tint' },
|
||||
{ partNumber: 'Glass5', color: 'Blue Tint' }
|
||||
]
|
||||
],
|
||||
['Rear', 'Stationary', 'Red Tint', [{ partNumber: 'Glass7', color: 'Red Tint' }]],
|
||||
[
|
||||
'Windshield',
|
||||
'Single',
|
||||
'Green Tint',
|
||||
[
|
||||
{ partNumber: 'Windshield1', color: 'Green Tint' },
|
||||
{ partNumber: 'Windshield2', color: 'Green Tint' }
|
||||
]
|
||||
],
|
||||
['Windshield', 'Single', 'Blue Tint', []],
|
||||
['Driver', 'Quarter', 'Green Tint', []]
|
||||
['Driver', 'Quarter', []]
|
||||
];
|
||||
|
||||
test.each(partsForSelectedTintTestCases)(
|
||||
'partsForSelectedTint returns correct parts',
|
||||
async (glassLocation, glassName, selectedTint, expectedResults) => {
|
||||
test.each(partsForLocationTestCases)(
|
||||
'partsForLocation returns correct parts',
|
||||
async (glassLocation, glassName, expectedResults) => {
|
||||
// Arrange
|
||||
const pageData = { partsOrQuestions: [
|
||||
{
|
||||
|
|
@ -219,11 +191,8 @@ describe('glass-part-question.vue', () => {
|
|||
pageData
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.setData({ selectedTint });
|
||||
|
||||
// Assert
|
||||
expect(expectedResults).toEqual(wrapper.vm.partsForSelectedTint);
|
||||
expect(wrapper.vm.partsForLocation).toEqual(expectedResults);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,44 +1,27 @@
|
|||
<template>
|
||||
<div class="row">
|
||||
<p class="mb-0 color-question-text">
|
||||
{{ colorQuestionText }}
|
||||
</p>
|
||||
<span class="glass-location-text">
|
||||
{{ partType }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="nested-radio">
|
||||
<div class="row my-2">
|
||||
<div class="col">
|
||||
<buttonQuestion
|
||||
v-model="selectedTint"
|
||||
:answers="tintSelectionOptions"
|
||||
buttonTypeString="listCard"
|
||||
:isWide="true"
|
||||
altText=""
|
||||
isRequired
|
||||
:groupName="replaceAllSpaceWithDash(`${glassLocation}-${glassName}`)"
|
||||
:validationRules="tintValidationRules">
|
||||
<div
|
||||
class="row my-2"
|
||||
aria-live="polite">
|
||||
<div class="col">
|
||||
<buttonQuestion
|
||||
id="glass-part-question"
|
||||
v-model="selectedPartNumber"
|
||||
buttonTypeString="radio"
|
||||
class="radioQuestion"
|
||||
:questionText="glassFeatureQuestion"
|
||||
:answers="featureListData[selectedTint]"
|
||||
textPosition="text-start"
|
||||
:loaderEnabled="false"
|
||||
isRequired
|
||||
isSmallQuestionText
|
||||
:groupName="replaceAllSpaceWithDash(`${glassLocation}-${glassName}-${selectedTint}`)"
|
||||
:validationRules="partValidationRules" />
|
||||
</div>
|
||||
</div>
|
||||
</buttonQuestion>
|
||||
</div>
|
||||
<template
|
||||
v-for="(tint, index) in tintSelectionOptions"
|
||||
:key="index">
|
||||
<div class="tint-option">
|
||||
<span>{{ `${partType} - ${tint}` }}</span>
|
||||
<span :class="`tint-image ${convertTintToCSSClass(tint)}`"></span>
|
||||
</div>
|
||||
</div>
|
||||
<buttonQuestion
|
||||
id="glass-part-question"
|
||||
v-model="selectedPartNumber"
|
||||
buttonTypeString="list-button"
|
||||
class="radioQuestion"
|
||||
:answers="featureListData[tint]"
|
||||
:loaderEnabled="false"
|
||||
isRequired
|
||||
:groupName="replaceAllSpaceWithDash(`${glassLocation}-${glassName}`)"
|
||||
:validationRules="partValidationRules" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -46,12 +29,10 @@
|
|||
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||
|
||||
// Supporting files
|
||||
import getTintImage from '@/constants/tint-mapper';
|
||||
import getCustomTransformValue from '@/constants/dynamictext-mapper';
|
||||
import { defineRule } from 'vee-validate';
|
||||
import { required } from '@/helpers/validation-rules';
|
||||
import errorMessages from '@/constants/error-messages';
|
||||
import { useMainStore } from '@/store';
|
||||
import { toTitleCase } from '@/helpers/text-helper';
|
||||
|
||||
export default {
|
||||
name: 'glass-part-question',
|
||||
|
|
@ -72,42 +53,21 @@ export default {
|
|||
emits: ['update:modelValue'],
|
||||
data() {
|
||||
return {
|
||||
glassColorQuestion: '',
|
||||
glassFeatureQuestion: '',
|
||||
selectedTint: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tintValidationRules() {
|
||||
const validationRuleName = `${this.glassLocation}-${this.glassName}-tint-required`;
|
||||
|
||||
defineRule(validationRuleName, required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
return validationRuleName;
|
||||
},
|
||||
partValidationRules() {
|
||||
const validationRuleName = `${this.glassLocation}-${this.glassName}-part-required`;
|
||||
|
||||
defineRule(validationRuleName, required(errorMessages.OPTION_REQUIRED));
|
||||
return validationRuleName;
|
||||
},
|
||||
colorQuestionText() {
|
||||
return getCustomTransformValue(
|
||||
this.glassColorQuestion,
|
||||
`${this.glassLocation} ${this.glassName}`
|
||||
);
|
||||
},
|
||||
|
||||
tintSelectionOptions() {
|
||||
const tintOptions = [];
|
||||
|
||||
Object.keys(this.featureListData).forEach((tintOption) => {
|
||||
const buttonImage = this.getTintSourceImage(this.glassLocation, tintOption);
|
||||
tintOptions.push({
|
||||
value: tintOption,
|
||||
buttonLabel: tintOption,
|
||||
buttonImage: buttonImage ? require(`@/assets/img/tints/${buttonImage}`) : null
|
||||
});
|
||||
tintOptions.push(tintOption);
|
||||
});
|
||||
|
||||
return tintOptions;
|
||||
|
|
@ -115,21 +75,21 @@ export default {
|
|||
|
||||
selectedPartNumber: {
|
||||
get() {
|
||||
return this.modelValue?.partNumber;
|
||||
return this.modelValue?.partNumber ?? '';
|
||||
},
|
||||
set(newValue) {
|
||||
const part = this.partsForSelectedTint
|
||||
.filter((partItem) => partItem.partNumber === newValue?.value)[0];
|
||||
const part = this.partsForLocation
|
||||
.filter((partItem) => partItem.partNumber === newValue)[0];
|
||||
this.$emit('update:modelValue', part);
|
||||
}
|
||||
},
|
||||
|
||||
partsForSelectedTint() {
|
||||
partsForLocation() {
|
||||
const matchingGlass = this.PartDataFromApi.partsOrQuestions?.filter((dataForGlassLocationAndName) =>
|
||||
dataForGlassLocationAndName.glassName === this.glassName
|
||||
&& dataForGlassLocationAndName.glassLocation === this.glassLocation);
|
||||
const matchingGlassParts = matchingGlass?.length === 1 ? matchingGlass[0].parts : [];
|
||||
return matchingGlassParts.filter((part) => part.color === this.selectedTint) ?? [];
|
||||
return matchingGlassParts;
|
||||
},
|
||||
|
||||
// Creates a map of the feature list data in the correct Name/Value
|
||||
|
|
@ -142,7 +102,7 @@ export default {
|
|||
|
||||
// Map the data to Name/Text object for ButtonQuestion
|
||||
const mappedItem = item.FeatureAnswers.reduce((featureArr, it) => {
|
||||
featureArr.Text = it.FeatureAnswerText; // Display to User
|
||||
featureArr.Text = this.toTitleCaseWithExceptions(it.FeatureAnswerText); // Display to User
|
||||
featureArr.Name = it.PartNumber; // Backing Value
|
||||
|
||||
return featureArr;
|
||||
|
|
@ -158,72 +118,19 @@ export default {
|
|||
|
||||
PartDataFromApi() {
|
||||
return this.mainStore.pageData(this.$route.query.issPage) ?? {};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedTint() {
|
||||
this.AutoSelectIfSinglePart();
|
||||
},
|
||||
|
||||
partType() {
|
||||
const type = this.partsForLocation?.[0]?.partType ?? '';
|
||||
return toTitleCase(type);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.LoadPreselectedValues();
|
||||
this.AutoSelectIfSinglePart();
|
||||
},
|
||||
methods: {
|
||||
// Initialize the component data
|
||||
initializeComponent(cmsContent) {
|
||||
this.glassColorQuestion = cmsContent.ColorQuestionWidget.QuestionText;
|
||||
this.glassFeatureQuestion = cmsContent.FeatureQuestionWidget.QuestionText;
|
||||
},
|
||||
|
||||
// Gets tint images based on the glass type, and tint name.
|
||||
// Returns an empty string if the src or object is undefined.
|
||||
getTintSourceImage(glassLocation, tintColor) {
|
||||
const tintSourceObject = getTintImage(glassLocation, tintColor);
|
||||
|
||||
if (tintSourceObject === undefined || !tintSourceObject.src) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return tintSourceObject.src;
|
||||
},
|
||||
|
||||
AutoSelectIfSinglePart() {
|
||||
if (this.partsForSelectedTint?.length > 0) {
|
||||
// Check if only a single part is present for the tint and set the v-model if it is.
|
||||
if (this.partsForSelectedTint?.length === 1) {
|
||||
// select element with matching partNumber
|
||||
this.updateSelectedPartNumber(this.partsForSelectedTint[0].partNumber);
|
||||
} else {
|
||||
this.selectedPartNumber = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Loads the preselected values from the store.
|
||||
LoadPreselectedValues() {
|
||||
this.$nextTick(() => {
|
||||
// Populate button-question model-value if parts data already exists in store
|
||||
if (this.modelValue !== undefined) {
|
||||
this.selectedTint = this.modelValue.color;
|
||||
const { glassParts } = useMainStore().order.lineItems;
|
||||
if (glassParts) {
|
||||
for (const tintPart of this.partsForSelectedTint) {
|
||||
for (const glassPart of glassParts) {
|
||||
if (tintPart.partNumber === glassPart.partNumber) {
|
||||
this.$nextTick(() => {
|
||||
this.updateSelectedPartNumber(tintPart.partNumber);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
updateSelectedPartNumber(partNumber) {
|
||||
this.selectedPartNumber = { value: partNumber };
|
||||
this.selectedPartNumber = partNumber;
|
||||
this.$nextTick(() => {
|
||||
const radioInput = document.querySelector(`input[value=${this.selectedPartNumber}]`);
|
||||
// Fire a click event on the input so the field is updated
|
||||
|
|
@ -231,37 +138,128 @@ export default {
|
|||
});
|
||||
},
|
||||
|
||||
AutoSelectIfSinglePart() {
|
||||
if (this.partsForLocation?.length > 0) {
|
||||
// Check if only a single part is present for the tint and set the v-model if it is.
|
||||
if (this.partsForLocation?.length === 1) {
|
||||
// select element with matching partNumber
|
||||
this.updateSelectedPartNumber(this.partsForLocation[0].partNumber);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
replaceAllSpaceWithDash(str) {
|
||||
return String(str).replaceAll(' ', '-');
|
||||
},
|
||||
convertTintToCSSClass(tint) {
|
||||
return tint.toLowerCase().replaceAll(' ', '-').replaceAll(',', '');
|
||||
},
|
||||
toTitleCaseWithExceptions(text) {
|
||||
const exceptions = ['side', 'glass', 'dimming'];
|
||||
let temp = toTitleCase(text);
|
||||
exceptions.forEach((exception) => {
|
||||
const regEx = new RegExp(exception, 'ig');
|
||||
temp = temp.replace(regEx, exception);
|
||||
});
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nested-radio {
|
||||
:deep(.ui-radio) {
|
||||
flex-direction: column;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.color-question-text {
|
||||
color: $black;
|
||||
.glass-location-text {
|
||||
font-weight: $font-weight-bold;
|
||||
color: $black;
|
||||
}
|
||||
.tint-option {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
height: 2rem;
|
||||
margin-top: .5rem;
|
||||
margin-bottom: .25rem;
|
||||
font-weight: 600;
|
||||
color: $black;
|
||||
|
||||
#glass-part-question {
|
||||
span.fw-bold.w-100 {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
@include media-breakpoint-up(md) {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
div.col.radio-button-container {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.radioQuestion {
|
||||
:deep(.list-button) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
.tint-image {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin-left: 1rem;
|
||||
margin-top: -.25rem;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url(~@/assets/img/tints/GlassTint.png);
|
||||
}
|
||||
.blue-tint-green-shade {
|
||||
background-position: -53px -53px;
|
||||
}
|
||||
.blue-tint-blue-shade {
|
||||
background-position: -160px -53px;
|
||||
}
|
||||
.gray-tint-gray-shade {
|
||||
background-position: -267px -53px;
|
||||
}
|
||||
.blue-tint {
|
||||
background-position: -373px -53px;
|
||||
}
|
||||
.gray-tint {
|
||||
background-position: -480px -53px;
|
||||
}
|
||||
.green-tint-green-shade {
|
||||
background-position: -53px -160px;
|
||||
}
|
||||
.green-tint-blue-shade {
|
||||
background-position: -160px -160px;
|
||||
}
|
||||
.green-tint-gray-shade {
|
||||
background-position: -267px -160px;
|
||||
}
|
||||
.green-tint {
|
||||
background-position: -373px -160px;
|
||||
}
|
||||
.clear {
|
||||
background-position: -480px -160px;
|
||||
}
|
||||
.bronze-tint-green-shade {
|
||||
background-position: -53px -266px;
|
||||
}
|
||||
.bronze-tint-blue-shade {
|
||||
background-position: -160px -266px;
|
||||
}
|
||||
.bronze-tint-gray-shade {
|
||||
background-position: -267px -266px;
|
||||
}
|
||||
.bronze-tint-bronze-shade {
|
||||
background-position: -373px -266px;
|
||||
}
|
||||
.bronze-tint {
|
||||
background-position: -480px -266px;
|
||||
}
|
||||
.bronze-tint-privacy {
|
||||
background-position: -587px -266px;
|
||||
}
|
||||
.clear-blue-shade {
|
||||
background-position: -53px -373px;
|
||||
}
|
||||
.gray-tint-blue-shade {
|
||||
background-position: -160px -373px;
|
||||
}
|
||||
.blue-tint-gray-shade {
|
||||
background-position: -267px -373px;
|
||||
}
|
||||
.gray-tint-privacy {
|
||||
background-position: -373px -373px;
|
||||
}
|
||||
.tinted {
|
||||
background-position: -480px -373px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -212,6 +212,7 @@ describe('vehicle-parts.vue', () => {
|
|||
|
||||
test('User had part questions > navigateBack triggers a router.navigateWithoutSaving change with correct scenario', async () => {
|
||||
// Arrange
|
||||
useMainStore().damage.glassToReplace = [{ glassLocation: 'Rear', glassName: 'Stationary' }, { glassLocation: 'Windshield', glassName: 'Single' }];
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
|
|
@ -244,6 +245,7 @@ describe('vehicle-parts.vue', () => {
|
|||
|
||||
test('User did not have part questions > navigateBack triggers a router.navigate change with correct scenario', async () => {
|
||||
// Arrange
|
||||
useMainStore().damage.glassToReplace = [{ glassLocation: 'Rear', glassName: 'Stationary' }, { glassLocation: 'Windshield', glassName: 'Single' }];
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue