diff --git a/webroot/.eslintrc.js b/webroot/.eslintrc.js index 377253b03..55b42d65c 100644 --- a/webroot/.eslintrc.js +++ b/webroot/.eslintrc.js @@ -97,6 +97,7 @@ module.exports = { }], 'vue/multi-word-component-names': OFF, 'vue/no-multiple-template-root': OFF, + 'vue/no-v-for-template-key': OFF, 'prefer-regex-literals': OFF, 'no-promise-executor-return': OFF, }, diff --git a/webroot/src/components/CompactSelector/CompactSelector.ts b/webroot/src/components/CompactSelector/CompactSelector.ts index 95f95b5f0..8db49ff50 100644 --- a/webroot/src/components/CompactSelector/CompactSelector.ts +++ b/webroot/src/components/CompactSelector/CompactSelector.ts @@ -5,7 +5,7 @@ // Created by InspiringApps on 10/2/2024. // -import { AppModes, compacts as compactsConfig } from '@/app.config'; +import { compacts as compactsConfig } from '@/app.config'; import { Component, mixins, @@ -146,11 +146,6 @@ class CompactSelector extends mixins(MixinForm) { } else { // Refresh the compact type on the store await this.$store.dispatch('user/setCurrentCompact', CompactSerializer.fromServer({ type: selectedCompactType })); - if (selectedCompactType === CompactType.COSMETOLOGY) { - this.$store.dispatch('setAppMode', AppModes.COSMETOLOGY); - } else { - this.$store.dispatch('setAppMode', AppModes.JCC); - } } } diff --git a/webroot/src/components/Forms/InputDate/InputDate.ts b/webroot/src/components/Forms/InputDate/InputDate.ts index 27e361dc3..098bde575 100644 --- a/webroot/src/components/Forms/InputDate/InputDate.ts +++ b/webroot/src/components/Forms/InputDate/InputDate.ts @@ -109,10 +109,12 @@ class InputDate extends mixins(MixinInput) { const { formInput } = this; formInput.validate = () => { + const { localValue, dateRaw } = this; const { validation } = formInput; + // Date format validation if (validation && (validation as any).validate) { - const result = (validation as any).validate(this.localValue); + const result = (validation as any).validate(localValue); if (result.error) { formInput.isValid = false; @@ -128,6 +130,15 @@ class InputDate extends mixins(MixinInput) { formInput.errorMessage = ''; formInput.isValid = true; } + + // Date existance validation + if (formInput.isValid && (localValue && !dateRaw)) { + formInput.isValid = false; + + if (formInput.isTouched) { + formInput.errorMessage = this.$t('inputErrors.invalidDate'); + } + } }; } diff --git a/webroot/src/components/Forms/_mixins/form.mixin.ts b/webroot/src/components/Forms/_mixins/form.mixin.ts index fcf45eec2..e1d03ff80 100644 --- a/webroot/src/components/Forms/_mixins/form.mixin.ts +++ b/webroot/src/components/Forms/_mixins/form.mixin.ts @@ -109,9 +109,13 @@ class MixinForm extends Vue { 'boolean.base': this.$t('inputErrors.required'), 'any.invalid': this.$t('inputErrors.required'), }, + date: { + 'date.invalid': this.$t('inputErrors.invalidDate'), + }, dateWithFormat: (format: string) => ({ 'any.required': this.$t('inputErrors.required'), 'string.empty': this.$t('inputErrors.required'), + 'date.invalid': this.$t('inputErrors.invalidDate'), 'string.base': this.$t('inputErrors.invalidDateWithFormat', { format }), 'string.pattern.base': this.$t('inputErrors.invalidDateWithFormat', { format }), }), diff --git a/webroot/src/components/Licensee/LicenseeList/LicenseeList.less b/webroot/src/components/Licensee/LicenseeList/LicenseeList.less index e6fddc300..fea52e3e8 100644 --- a/webroot/src/components/Licensee/LicenseeList/LicenseeList.less +++ b/webroot/src/components/Licensee/LicenseeList/LicenseeList.less @@ -26,6 +26,7 @@ align-content: center; align-items: center; justify-content: center; + order: 1; width: 100%; margin-top: 1.2rem; padding: 0.4rem 1rem; @@ -34,7 +35,7 @@ background-color: @darkBlue; @media @desktopWidth { - order: 1; + order: 0; width: auto; max-width: 75%; margin-top: 0; diff --git a/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts b/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts index 5403978c4..7c6154ce0 100644 --- a/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts +++ b/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts @@ -85,6 +85,14 @@ class LicenseeList extends Vue { return this.$store.state.license; } + get isAppModeJcc(): boolean { + return this.$store.getters.isAppModeJcc; + } + + get isAppModeCosmetology(): boolean { + return this.$store.getters.isAppModeCosmetology; + } + get licenseStoreRecordCount(): number { return this.licenseStore.model?.length || 0; } @@ -103,6 +111,12 @@ class LicenseeList extends Vue { return `${firstName} ${lastName}`.trim(); } + get searchDisplayDob(): string { + const { dob = '' } = this.searchParams; + + return (dob) ? `${this.$t('common.dateOfBirthShort')}: ${moment(dob, serverDateFormat).format(displayDateFormat)}`.trim() : ''; + } + get searchDisplayHomeState(): string { const { homeState } = this.searchParams; @@ -189,17 +203,25 @@ class LicenseeList extends Vue { return (npi) ? `${this.$t('licensing.npi')}: ${npi}`.trim() : ''; } + get searchDisplayLicenseNumber(): string { + const { licenseNumber = '' } = this.searchParams; + + return (licenseNumber) ? `${this.$t('licensing.licenseNumSymbol')}: ${licenseNumber}`.trim() : ''; + } + get searchDisplayAll(): string { const joined = [ this.searchDisplayCompact, this.searchDisplayFullName, + this.searchDisplayDob, this.searchDisplayHomeState, this.searchDisplayPrivilegeState, this.searchDisplayPrivilegePurchaseDates, this.searchDisplayMilitaryStatus, this.searchDisplayInvestigationStatus, this.searchDisplayEncumberDates, - this.searchDisplayNpi + this.searchDisplayNpi, + this.searchDisplayLicenseNumber ].join(', ').trim(); return joined.replace(/(^[,\s]+)|([,\s]+$)/g, '').replace(/(,\s)\1+/g, ', '); // Replace repeated commas with single comma @@ -218,7 +240,14 @@ class LicenseeList extends Vue { firstName: this.$t('common.firstName'), lastName: this.$t('common.lastName'), homeJurisdictionDisplay: () => this.$t('licensing.homeState'), - privilegeStatesDisplay: () => this.$t('licensing.privileges'), + ...(this.isAppModeCosmetology + ? { + licenseNumber: this.$t('licensing.stateLicenseNumber'), + } + : { + privilegeStatesDisplay: () => this.$t('licensing.privileges'), + } + ), }; return record; @@ -343,6 +372,9 @@ class LicenseeList extends Vue { if (searchParams?.lastName) { requestConfig.licenseeLastName = searchParams.lastName; } + if (searchParams?.dob) { + requestConfig.dob = searchParams.dob; + } if (searchParams?.homeState) { requestConfig.homeState = searchParams.homeState.toLowerCase(); } @@ -370,6 +402,9 @@ class LicenseeList extends Vue { if (searchParams?.npi) { requestConfig.npi = searchParams.npi; } + if (searchParams?.licenseNumber) { + requestConfig.licenseNumber = searchParams.licenseNumber; + } // Paging params if (!isDirectExport) { diff --git a/webroot/src/components/Licensee/LicenseeList/LicenseeList.vue b/webroot/src/components/Licensee/LicenseeList/LicenseeList.vue index a266e9188..b92853796 100644 --- a/webroot/src/components/Licensee/LicenseeList/LicenseeList.vue +++ b/webroot/src/components/Licensee/LicenseeList/LicenseeList.vue @@ -21,22 +21,23 @@

{{ $t('licensing.licensingListTitle') }}

-
{{ $t('common.viewing') }}: {{ searchDisplayAll }}
+
{{ $t('licensing.licensingListDescription')}}
diff --git a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.less b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.less index 9f25908f2..87acb1ef3 100644 --- a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.less +++ b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.less @@ -26,6 +26,7 @@ align-content: center; align-items: center; justify-content: center; + order: 1; width: 100%; margin-top: 1.2rem; padding: 0.4rem 1rem; @@ -34,7 +35,7 @@ background-color: @darkBlue; @media @desktopWidth { - order: 1; + order: 0; width: auto; margin-top: 0; margin-right: 2.4rem; diff --git a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts index 7ab3fb689..363a6cec4 100644 --- a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts +++ b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts @@ -85,6 +85,14 @@ class LicenseeList extends Vue { return this.$store.state.license; } + get isAppModeJcc(): boolean { + return this.$store.getters.isAppModeJcc; + } + + get isAppModeCosmetology(): boolean { + return this.$store.getters.isAppModeCosmetology; + } + get licenseStoreRecordCount(): number { return this.licenseStore.model?.length || 0; } @@ -97,58 +105,33 @@ class LicenseeList extends Vue { return (this.isPublicSearch) ? this.userStore.currentCompact?.abbrev() || '' : ''; } - get searchDisplayFirstName(): string { - const delimiter = (this.searchDisplayCompact) ? ', ' : ''; - let displayFirstName = ''; + get searchDisplayFullName(): string { + const { firstName = '', lastName = '' } = this.searchParams; - if (this.searchParams.firstName) { - displayFirstName = `${delimiter}${this.searchParams.firstName}` || ''; - } - - return displayFirstName; - } - - get searchDisplayLastName(): string { - const delimiter = (this.searchDisplayCompact && !this.searchDisplayFirstName) ? ', ' : ''; - const subDelimiter = (this.searchDisplayFirstName) ? ' ' : ''; - let displayLastName = ''; - - if (this.searchParams.lastName) { - displayLastName = `${delimiter}${subDelimiter}${this.searchParams.lastName}` || ''; - } - - return displayLastName; + return `${firstName} ${lastName}`.trim(); } get searchDisplayState(): string { const { state } = this.searchParams; - const { searchDisplayCompact, searchDisplayFirstName, searchDisplayLastName } = this; - const delimiter = (searchDisplayCompact || searchDisplayFirstName || searchDisplayLastName) ? ', ' : ''; - let displayState = ''; - if (state) { - const stateModel = new State({ abbrev: state }); + return (state) ? `${new State({ abbrev: state }).name()}` : ''; + } - displayState = `${delimiter}${stateModel.name()}`; - } + get searchDisplayLicenseNumber(): string { + const { licenseNumber = '' } = this.searchParams; - return displayState; + return (licenseNumber) ? `${this.$t('licensing.licenseNumSymbol')}: ${licenseNumber}`.trim() : ''; } get searchDisplayAll(): string { - const { - searchDisplayCompact, - searchDisplayFirstName, - searchDisplayLastName, - searchDisplayState - } = this; - return [ - searchDisplayCompact, - searchDisplayFirstName, - searchDisplayLastName, - searchDisplayState - ].join('').trim(); + this.searchDisplayCompact, + this.searchDisplayFullName, + this.searchDisplayState, + this.searchDisplayLicenseNumber + ] + .filter((displayPart) => !!displayPart?.trim()) + .join(', ').trim(); } get sortOptions(): Array { @@ -168,9 +151,15 @@ class LicenseeList extends Vue { const record = { firstName: this.$t('common.firstName'), lastName: this.$t('common.lastName'), - ssnMaskedPartial: () => this.$t('licensing.ssn'), homeJurisdictionDisplay: () => this.$t('licensing.homeState'), - privilegeStatesDisplay: () => this.$t('licensing.privileges'), + ...(this.isAppModeCosmetology + ? { + licenseNumber: this.$t('licensing.stateLicenseNumber'), + } + : { + privilegeStatesDisplay: () => this.$t('licensing.privileges'), + } + ), }; return record; @@ -307,6 +296,9 @@ class LicenseeList extends Vue { if (searchParams?.state) { requestConfig.jurisdiction = searchParams.state.toLowerCase(); } + if (this.isAppModeCosmetology && searchParams?.licenseNumber) { + requestConfig.licenseNumber = searchParams.licenseNumber; + } // Make fetch request await this.$store.dispatch('license/getLicenseesRequest', { diff --git a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.vue b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.vue index d5a083554..402735aee 100644 --- a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.vue +++ b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.vue @@ -20,22 +20,23 @@

{{ $t('licensing.licensingListTitle') }}

-
{{ $t('common.viewing') }}: {{ searchDisplayAll }}
+
{{ $t('licensing.licensingListDescription')}}
diff --git a/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.ts b/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.ts index 50792dd49..692f5ee46 100644 --- a/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.ts +++ b/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.ts @@ -42,6 +42,14 @@ class LicenseeRow extends Vue { return this.$store.state.sorting; } + get isAppModeJcc(): boolean { + return this.$store.getters.isAppModeJcc; + } + + get isAppModeCosmetology(): boolean { + return this.$store.getters.isAppModeCosmetology; + } + get sortingStoreOption(): any { return this.sortingStore.sortingMap[this.listId]?.option; } diff --git a/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.vue b/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.vue index 83f82badf..7110c6fa4 100644 --- a/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.vue +++ b/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.vue @@ -63,6 +63,7 @@ }">
+
+ {{ $t('licensing.stateLicenseNumber') }}: + + + +
diff --git a/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.ts b/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.ts index 13c33c34b..d58d70cfd 100644 --- a/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.ts +++ b/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.ts @@ -18,6 +18,7 @@ import { ComputedRef, nextTick } from 'vue'; +import { AppModes, dateFormatPatterns } from '@/app.config'; import MixinForm from '@components/Forms/_mixins/form.mixin'; import InputRadioGroup from '@components/Forms/InputRadioGroup/InputRadioGroup.vue'; import InputText from '@components/Forms/InputText/InputText.vue'; @@ -44,6 +45,7 @@ export interface LicenseSearch { firstName?: string; lastName?: string; homeState?: string; + dob?: string; privilegeState?: string; privilegePurchaseStartDate?: string; privilegePurchaseEndDate?: string; @@ -52,6 +54,7 @@ export interface LicenseSearch { encumberStartDate?: string; encumberEndDate?: string; npi?: string; + licenseNumber?: string; } @Component({ @@ -87,6 +90,10 @@ class LicenseeSearch extends mixins(MixinForm) { // // Computed // + get globalStore() { + return this.$store.state; + } + get licenseStore(): any { return this.$store.state.license; } @@ -95,6 +102,18 @@ class LicenseeSearch extends mixins(MixinForm) { return this.$store.state.user; } + get appMode(): AppModes { + return this.globalStore.appMode; + } + + get isAppModeJcc(): boolean { + return this.$store.getters.isAppModeJcc; + } + + get isAppModeCosmetology(): boolean { + return this.$store.getters.isAppModeCosmetology; + } + get compactType(): CompactType | null { return this.userStore.currentCompact?.type; } @@ -236,6 +255,26 @@ class LicenseeSearch extends mixins(MixinForm) { value: this.searchParams.homeState || '', isDisabled: computed(() => this.enableCompactSelect && !this.compactType), }), + licenseNumber: new FormInput({ + id: 'license-number', + name: 'license-number', + label: computed(() => this.$t('licensing.licenseNumber')), + placeholder: '', + validation: Joi.string().min(0).max(100).messages(this.joiMessages.string), + value: this.searchParams.licenseNumber || '', + enforceMax: true, + }), + dob: new FormInput({ + id: 'dob', + name: 'dob', + label: computed(() => this.$t('common.dateOfBirth')), + placeholder: computed(() => 'MM/DD/YYYY'), + validation: Joi.string() + .allow(null, '') + .pattern(dateFormatPatterns.MM_DD_YYYY) + .messages(this.joiMessages.dateWithFormat('MM/DD/YYYY')), + value: this.searchParams.dob || '', + }), privilegeState: new FormInput({ id: 'privilege-state', name: 'privilege-state', @@ -331,24 +370,36 @@ class LicenseeSearch extends mixins(MixinForm) { if (this.isFormValid) { this.startFormLoading(); - const allowedSearchProps = [ + const searchProps: LicenseSearch = { + searchType: this.selectedSearchType || SearchTypes.PROVIDER, + isDirectExport: this.isSearchByPrivileges, + }; + const allowedSearchProps = [ // Common search props 'compact', 'firstName', 'lastName', 'homeState', - 'privilegeState', - 'privilegePurchaseStartDate', - 'privilegePurchaseEndDate', - 'militaryStatus', 'investigationStatus', 'encumberStartDate', 'encumberEndDate', - 'npi', ]; - const searchProps: LicenseSearch = { - searchType: this.selectedSearchType || SearchTypes.PROVIDER, - isDirectExport: this.isSearchByPrivileges, - }; + + // Per compact search props + switch (this.appMode) { + case AppModes.JCC: + allowedSearchProps.push('privilegeState'); + allowedSearchProps.push('privilegePurchaseStartDate'); + allowedSearchProps.push('privilegePurchaseEndDate'); + allowedSearchProps.push('militaryStatus'); + allowedSearchProps.push('npi'); + break; + case AppModes.COSMETOLOGY: + allowedSearchProps.push('licenseNumber'); + allowedSearchProps.push('dob'); + break; + default: + break; + } allowedSearchProps.forEach((searchProp) => { searchProps[searchProp] = this.formValues[searchProp]; }); this.$emit('searchParams', searchProps); @@ -369,6 +420,8 @@ class LicenseeSearch extends mixins(MixinForm) { this.formData.encumberStartDate.value = ''; this.formData.encumberEndDate.value = ''; this.formData.npi.value = ''; + this.formData.licenseNumber.value = ''; + this.formData.dob.value = ''; this.isFormLoading = false; this.isFormSuccessful = false; this.isFormError = false; @@ -380,14 +433,20 @@ class LicenseeSearch extends mixins(MixinForm) { this.formData.firstName.value = 'Test'; this.formData.lastName.value = 'User'; this.formData.homeState.value = 'co'; - this.formData.privilegeState.value = 'co'; - this.formData.privilegePurchaseStartDate.value = moment().startOf('month').format('YYYY-MM-DD'); - this.formData.privilegePurchaseEndDate.value = moment().endOf('month').format('YYYY-MM-DD'); - this.formData.militaryStatus.value = 'approved'; this.formData.investigationStatus.value = 'underInvestigation'; this.formData.encumberStartDate.value = moment().startOf('month').format('YYYY-MM-DD'); this.formData.encumberEndDate.value = moment().endOf('month').format('YYYY-MM-DD'); - this.formData.npi.value = 'ABC123'; + + if (this.isAppModeJcc) { + this.formData.privilegeState.value = 'co'; + this.formData.privilegePurchaseStartDate.value = moment().startOf('month').format('YYYY-MM-DD'); + this.formData.privilegePurchaseEndDate.value = moment().endOf('month').format('YYYY-MM-DD'); + this.formData.militaryStatus.value = 'approved'; + this.formData.npi.value = 'ABC123'; + } else if (this.isAppModeCosmetology) { + this.formData.licenseNumber.value = 'ABC123'; + this.formData.dob.value = moment('1970-01-01').format('YYYY-MM-DD'); + } this.validateAll({ asTouched: true }); await nextTick(); diff --git a/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.vue b/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.vue index eb8d80ceb..b9e92efd7 100644 --- a/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.vue +++ b/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.vue @@ -34,7 +34,7 @@ @input="updateCurrentCompact" /> -
+
-
+
+ +
+
+ +
+
-
+
-
+
-
+
-
+
this.enableCompactSelect && !this.compactType), }), + licenseNumber: new FormInput({ + id: 'license-number', + name: 'license-number', + label: computed(() => this.$t('licensing.licenseNumber')), + placeholder: '', + validation: Joi.string().min(0).max(100).messages(this.joiMessages.string), + value: this.searchParams.licenseNumber || '', + enforceMax: true, + }), submit: new FormInput({ isSubmitInput: true, id: 'submit', @@ -158,6 +195,7 @@ class LicenseeSearch extends mixins(MixinForm) { if (this.enableCompactSelect) { await this.$store.dispatch('user/setCurrentCompact', CompactSerializer.fromServer({ type: selectedCompactType.value })); state.value = ''; + this.validateAll({ asTouched: false }); } } @@ -168,13 +206,19 @@ class LicenseeSearch extends mixins(MixinForm) { if (this.isFormValid) { this.startFormLoading(); + const searchProps: LicenseSearchLegacy = {}; const allowedSearchProps = [ + // Common search props 'compact', 'firstName', 'lastName', 'state' ]; - const searchProps: LicenseSearchLegacy = {}; + + // Per compact search props + if (this.appMode === AppModes.COSMETOLOGY) { + allowedSearchProps.push('licenseNumber'); + } allowedSearchProps.forEach((searchProp) => { searchProps[searchProp] = this.formValues[searchProp]; }); this.$emit('searchParams', searchProps); @@ -185,20 +229,64 @@ class LicenseeSearch extends mixins(MixinForm) { // Last name is currently optional overall, but required if first name is included; therefore needs this non-trivial validation logic customValidateLastName(asTouched = true): void { + const { isAppModeJcc } = this; const { firstName, lastName } = this.formData; const shouldSkip = (asTouched) ? false : !lastName.isTouched; - if (!shouldSkip && firstName.value && !lastName.value) { - lastName.isValid = false; - lastName.errorMessage = this.$t('inputErrors.lastNameRequired'); - } else if (!lastName.isValid) { - lastName.isValid = true; - lastName.errorMessage = ''; + if (isAppModeJcc) { // Currently only JCC requires this check + if (!shouldSkip && firstName.value && !lastName.value) { + lastName.isValid = false; + lastName.errorMessage = this.$t('inputErrors.lastNameRequired'); + } else if (!lastName.isValid) { + lastName.isValid = true; + lastName.errorMessage = ''; + } } this.checkValidForAll(); } + async resetForm(): Promise { + if (this.enableCompactSelect) { + this.formData.compact.value = ''; + await this.$store.dispatch('user/setCurrentCompact', null); + } + + this.formData.firstName.value = ''; + this.formData.lastName.value = ''; + this.formData.state.value = ''; + this.formData.licenseNumber.value = ''; + this.isFormLoading = false; + this.isFormSuccessful = false; + this.isFormError = false; + this.updateFormSubmitSuccess(''); + this.updateFormSubmitError(''); + } + + async mockPopulate(): Promise { + if (this.enableCompactSelect) { + this.formData.compact.value = (this.isAppModeCosmetology) + ? CompactType.COSMETOLOGY + : CompactType.OT; + + await this.updateCurrentCompact(); + } + + this.formData.firstName.value = 'Test'; + this.formData.lastName.value = 'User'; + this.formData.state.value = 'co'; + + if (this.isAppModeCosmetology) { + this.formData.licenseNumber.value = 'ABC123'; + } + + this.validateAll({ asTouched: true }); + await nextTick(); + const submitButton = document.getElementById('submit'); + + submitButton?.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + // // Watch // diff --git a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.vue b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.vue index c6a82dc8c..c502bd772 100644 --- a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.vue +++ b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.vue @@ -12,6 +12,21 @@
{{ $t('licensing.searchTitle') }}
{{ $t('licensing.searchSubtext') }}
+
+ +
+
+
+ +
diff --git a/webroot/src/components/PrivilegeCard/PrivilegeCard.ts b/webroot/src/components/PrivilegeCard/PrivilegeCard.ts index b99ca7268..044cdfa70 100644 --- a/webroot/src/components/PrivilegeCard/PrivilegeCard.ts +++ b/webroot/src/components/PrivilegeCard/PrivilegeCard.ts @@ -87,6 +87,14 @@ class PrivilegeCard extends mixins(MixinForm) { return this.$store.state.user; } + get isAppModeJcc(): boolean { + return this.$store.getters.isAppModeJcc; + } + + get isAppModeCosmetology(): boolean { + return this.$store.getters.isAppModeCosmetology; + } + get currentUser(): StaffUser { return this.userStore.model; } diff --git a/webroot/src/components/PrivilegeCard/PrivilegeCard.vue b/webroot/src/components/PrivilegeCard/PrivilegeCard.vue index 8853f26af..529035a05 100644 --- a/webroot/src/components/PrivilegeCard/PrivilegeCard.vue +++ b/webroot/src/components/PrivilegeCard/PrivilegeCard.vue @@ -89,7 +89,7 @@
-
+
{{ $t('licensing.activeFrom') }}
{{ (isActive) ? activeFromContent : $t('licensing.deactivated') }}
@@ -97,16 +97,17 @@
{{expiresTitle}}
{{expiresContent}}
-
+
{{$t('licensing.privilegeNumSymbol')}}
{{privilegeId}}
-
+
{{ $t('licensing.disciplineStatus') }}
{{disciplineContent}}
-
+
{{ $t('licensing.privilegeId') }}
{{ privilegeId }}
@@ -269,7 +270,7 @@

{{ $t('licensing.confirmPrivilegeEncumberSuccess') }}

{{ licenseeName }}
-
{{ privilegeId }}
+
{{ privilegeId }}
-
+
{{ $t('licensing.privilegeId') }}
{{ privilegeId }}
@@ -485,7 +486,7 @@

{{ $t('licensing.confirmPrivilegeInvestigationStartSuccess') }}

{{ licenseeName }}
-
{{ privilegeId }}
+
{{ privilegeId }}
{{ $t('licensing.confirmPrivilegeInvestigationEndSuccess') }}
{{ licenseeName }}
-
{{ privilegeId }}
+
{{ privilegeId }}
{ - return this.licensee?.licenses || []; + return (this.licensee?.licenses || []).slice().sort(this.sortLicenses); } get isLoading(): boolean { @@ -148,7 +156,7 @@ export default class LicensingDetail extends Vue { } get licenseePrivileges(): Array { - return this.licensee?.privileges || []; + return (this.licensee?.privileges || []).slice().sort(this.sortPrivileges); } get licenseeStates(): Array { @@ -262,6 +270,54 @@ export default class LicensingDetail extends Vue { return false; } + sortLicenses(license1: License, license2: License): number { + let sort = this.sortByIssueState(license1, license2); + + if (sort === 0) { + sort = this.sortByLicenseType(license1, license2); + } + + return sort; + } + + sortPrivileges(privilege1: License, privilege2: License): number { + let sort = this.sortByLicenseType(privilege1, privilege2); + + if (sort === 0) { + sort = this.sortByIssueState(privilege1, privilege2); + } + + return sort; + } + + sortByLicenseType(license1: License, license2: License): number { + const licenseType1 = license1.licenseTypeAbbreviation(); + const licenseType2 = license2.licenseTypeAbbreviation(); + let sort = 0; + + if (licenseType1 < licenseType2) { + sort = -1; + } else if (licenseType1 > licenseType2) { + sort = 1; + } + + return sort; + } + + sortByIssueState(license1: License, license2: License): number { + const state1 = license1.issueState?.name().toLowerCase() || ''; + const state2 = license2.issueState?.name().toLowerCase() || ''; + let sort = 0; + + if (state1 < state2) { + sort = -1; + } else if (state1 > state2) { + sort = 1; + } + + return sort; + } + async revealFullSsn(): Promise { this.licenseeFullSsnLoading = true; this.licenseeFullSsn = ''; diff --git a/webroot/src/pages/LicensingDetail/LicensingDetail.vue b/webroot/src/pages/LicensingDetail/LicensingDetail.vue index 1d398a5cf..1c02d9622 100644 --- a/webroot/src/pages/LicensingDetail/LicensingDetail.vue +++ b/webroot/src/pages/LicensingDetail/LicensingDetail.vue @@ -135,7 +135,7 @@
- +
@@ -189,13 +189,18 @@ />
- +
diff --git a/webroot/src/store/global/global.getters.ts b/webroot/src/store/global/global.getters.ts index 91702047d..ed4404f8d 100644 --- a/webroot/src/store/global/global.getters.ts +++ b/webroot/src/store/global/global.getters.ts @@ -5,10 +5,13 @@ // Created by InspiringApps on 4/12/20. // -// import { State } from './global.state'; +import { AppModes } from '@/app.config'; +import { State } from './global.state'; export default { // isLoading: (state: State) => state.isLoading, // error: (state: State) => state.error, // isModalOpen: (state: State) => state.isModalOpen, + isAppModeJcc: (state: State) => state.appMode === AppModes.JCC, + isAppModeCosmetology: (state: State) => state.appMode === AppModes.COSMETOLOGY, }; diff --git a/webroot/src/store/license/license.mutations.ts b/webroot/src/store/license/license.mutations.ts index e6ce2f2c3..9631cefb3 100644 --- a/webroot/src/store/license/license.mutations.ts +++ b/webroot/src/store/license/license.mutations.ts @@ -110,6 +110,7 @@ export default { firstName: '', lastName: '', state: '', + licenseNumber: '', }; }, [MutationTypes.STORE_RESET_LICENSE]: (state: any) => { @@ -122,6 +123,7 @@ export default { firstName: '', lastName: '', state: '', + licenseNumber: '', }; }, [MutationTypes.GET_PRIVILEGE_HISTORY_REQUEST]: (state: any) => { diff --git a/webroot/src/store/license/license.spec.ts b/webroot/src/store/license/license.spec.ts index 3364f7860..288f43efa 100644 --- a/webroot/src/store/license/license.spec.ts +++ b/webroot/src/store/license/license.spec.ts @@ -157,6 +157,7 @@ describe('License Store Mutations', () => { firstName: 'test', lastName: 'test', state: 'test', + licenseNumber: 'test', }, }; @@ -167,6 +168,7 @@ describe('License Store Mutations', () => { firstName: '', lastName: '', state: '', + licenseNumber: '', }); }); it('should successfully reset license store', () => { @@ -180,6 +182,7 @@ describe('License Store Mutations', () => { firstName: 'test', lastName: 'test', state: 'test', + licenseNumber: 'test', }, }; @@ -195,6 +198,7 @@ describe('License Store Mutations', () => { firstName: '', lastName: '', state: '', + licenseNumber: '', }, }); }); diff --git a/webroot/src/store/user/user.actions.ts b/webroot/src/store/user/user.actions.ts index 840e71034..bd87647f9 100644 --- a/webroot/src/store/user/user.actions.ts +++ b/webroot/src/store/user/user.actions.ts @@ -16,7 +16,7 @@ import { autoLogoutConfig } from '@/app.config'; import localStorage from '@store/local.storage'; -import { Compact } from '@models/Compact/Compact.model'; +import { Compact, CompactType } from '@models/Compact/Compact.model'; import { PurchaseFlowStep } from '@/models/PurchaseFlowStep/PurchaseFlowStep.model'; import moment from 'moment'; import axios from 'axios'; @@ -178,6 +178,14 @@ export default { commit(MutationTypes.STORE_UPDATE_CURRENT_COMPACT, compact); if (compact?.type) { + // Set the AppMode based on compact + if (compact.type === CompactType.COSMETOLOGY) { + dispatch('setAppMode', AppModes.COSMETOLOGY, { root: true }); + } else { + dispatch('setAppMode', AppModes.JCC, { root: true }); + } + + // Fetch the states for the compact await dispatch('getCompactStatesRequest', { compact: compact.type }); } }, diff --git a/webroot/src/store/user/user.spec.ts b/webroot/src/store/user/user.spec.ts index fce3be555..481c4ef9c 100644 --- a/webroot/src/store/user/user.spec.ts +++ b/webroot/src/store/user/user.spec.ts @@ -1074,7 +1074,7 @@ describe('User Store Actions', async () => { expect(commit.firstCall.args).to.matchPattern([MutationTypes.STORE_UPDATE_CURRENT_COMPACT, null]); expect(dispatch.callCount, 'dispatch').to.equal(0); }); - it('should successfully set current compact (with compact)', async () => { + it('should successfully set current compact (with compact - jcc)', async () => { const commit = sinon.spy(); const dispatch = sinon.spy(); const compact = new Compact({ type: CompactType.ASLP }); @@ -1083,8 +1083,24 @@ describe('User Store Actions', async () => { expect(commit.calledOnce, 'commit').to.equal(true); expect(commit.firstCall.args).to.matchPattern([MutationTypes.STORE_UPDATE_CURRENT_COMPACT, compact]); - expect(dispatch.callCount, 'dispatch').to.equal(1); - expect([dispatch.firstCall.args[0]]).to.matchPattern(['getCompactStatesRequest']); + expect(dispatch.callCount, 'dispatch').to.equal(2); + expect([dispatch.firstCall.args[0]]).to.matchPattern(['setAppMode']); + expect([dispatch.firstCall.args[1]]).to.matchPattern([AppModes.JCC]); + expect([dispatch.secondCall.args[0]]).to.matchPattern(['getCompactStatesRequest']); + }); + it('should successfully set current compact (with compact - cosmetology)', async () => { + const commit = sinon.spy(); + const dispatch = sinon.spy(); + const compact = new Compact({ type: CompactType.COSMETOLOGY }); + + await actions.setCurrentCompact({ commit, dispatch }, compact); + + expect(commit.calledOnce, 'commit').to.equal(true); + expect(commit.firstCall.args).to.matchPattern([MutationTypes.STORE_UPDATE_CURRENT_COMPACT, compact]); + expect(dispatch.callCount, 'dispatch').to.equal(2); + expect([dispatch.firstCall.args[0]]).to.matchPattern(['setAppMode']); + expect([dispatch.firstCall.args[1]]).to.matchPattern([AppModes.COSMETOLOGY]); + expect([dispatch.secondCall.args[0]]).to.matchPattern(['getCompactStatesRequest']); }); it('should successfully start updateHomeJurisdictionRequest', async () => { const commit = sinon.spy();