From 14fb74853900ae9d9eee373910d66bc34af99ef9 Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Tue, 24 Feb 2026 15:35:07 -0700 Subject: [PATCH 01/15] WIP: Cosmo search updates --- .../CompactSelector/CompactSelector.ts | 7 +- .../Licensee/LicenseeList/LicenseeList.ts | 3 + .../LicenseeListLegacy/LicenseeListLegacy.ts | 3 + .../Licensee/LicenseeSearch/LicenseeSearch.ts | 74 +++++++++++++++---- .../LicenseeSearch/LicenseeSearch.vue | 18 +++-- .../LicenseeSearchLegacy.ts | 37 +++++++++- .../LicenseeSearchLegacy.vue | 6 ++ webroot/src/network/licenseApi/data.api.ts | 6 ++ webroot/src/network/searchApi/data.api.ts | 6 ++ webroot/src/store/global/global.getters.ts | 5 +- .../src/store/license/license.mutations.ts | 2 + webroot/src/store/license/license.spec.ts | 4 + webroot/src/store/user/user.actions.ts | 10 ++- webroot/src/store/user/user.spec.ts | 22 +++++- 14 files changed, 170 insertions(+), 33 deletions(-) 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/Licensee/LicenseeList/LicenseeList.ts b/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts index 5403978c4..9575610c1 100644 --- a/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts +++ b/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts @@ -370,6 +370,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/LicenseeListLegacy/LicenseeListLegacy.ts b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts index 7ab3fb689..08d0b5c36 100644 --- a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts +++ b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts @@ -307,6 +307,9 @@ class LicenseeList extends Vue { if (searchParams?.state) { requestConfig.jurisdiction = searchParams.state.toLowerCase(); } + if (searchParams?.licenseNumber) { + requestConfig.licenseNumber = searchParams.licenseNumber; + } // Make fetch request await this.$store.dispatch('license/getLicenseesRequest', { diff --git a/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.ts b/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.ts index 13c33c34b..3ab0e34ce 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 } 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'; @@ -52,6 +53,7 @@ export interface LicenseSearch { encumberStartDate?: string; encumberEndDate?: string; npi?: string; + licenseNumber?: string; } @Component({ @@ -87,6 +89,10 @@ class LicenseeSearch extends mixins(MixinForm) { // // Computed // + get globalStore() { + return this.$store.state; + } + get licenseStore(): any { return this.$store.state.license; } @@ -95,6 +101,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 +254,15 @@ 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, + }), privilegeState: new FormInput({ id: 'privilege-state', name: 'privilege-state', @@ -331,24 +358,35 @@ 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'); + break; + default: + break; + } allowedSearchProps.forEach((searchProp) => { searchProps[searchProp] = this.formValues[searchProp]; }); this.$emit('searchParams', searchProps); @@ -369,6 +407,7 @@ class LicenseeSearch extends mixins(MixinForm) { this.formData.encumberStartDate.value = ''; this.formData.encumberEndDate.value = ''; this.formData.npi.value = ''; + this.formData.licenseNumber.value = ''; this.isFormLoading = false; this.isFormSuccessful = false; this.isFormError = false; @@ -380,14 +419,19 @@ 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.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..2b592d5ca 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', @@ -168,13 +199,17 @@ class LicenseeSearch extends mixins(MixinForm) { if (this.isFormValid) { this.startFormLoading(); + const searchProps: LicenseSearchLegacy = {}; const allowedSearchProps = [ 'compact', 'firstName', 'lastName', 'state' ]; - const searchProps: LicenseSearchLegacy = {}; + + if (this.appMode === AppModes.COSMETOLOGY) { + allowedSearchProps.push('licenseNumber'); + } allowedSearchProps.forEach((searchProp) => { searchProps[searchProp] = this.formValues[searchProp]; }); this.$emit('searchParams', searchProps); diff --git a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.vue b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.vue index c6a82dc8c..51f9fb553 100644 --- a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.vue +++ b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.vue @@ -41,6 +41,12 @@ @blur="customValidateLastName(true)" />
+
+ +
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(); From ca6e3d8db800ff17ae5fbd7224c6fa663cb9a5ac Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Tue, 24 Feb 2026 16:19:01 -0700 Subject: [PATCH 02/15] WIP: Cosmo search updates --- .../Licensee/LicenseeList/LicenseeList.ts | 17 +++++++- .../LicenseeListLegacy/LicenseeListLegacy.ts | 18 ++++++++- .../Licensee/LicenseeRow/LicenseeRow.ts | 8 ++++ .../Licensee/LicenseeRow/LicenseeRow.vue | 18 +++++++++ .../components/PrivilegeCard/PrivilegeCard.ts | 8 ++++ .../PrivilegeCard/PrivilegeCard.vue | 9 +++-- webroot/src/locales/en.json | 1 + webroot/src/locales/es.json | 1 + webroot/src/models/Licensee/Licensee.model.ts | 2 +- webroot/src/network/mocks/mock.data.ts | 40 +++++++++++++++++++ 10 files changed, 114 insertions(+), 8 deletions(-) diff --git a/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts b/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts index 9575610c1..9b8cf1478 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; } @@ -218,7 +226,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; diff --git a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts index 08d0b5c36..17e141635 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; } @@ -168,9 +176,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; 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..c760ef69b 100644 --- a/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.vue +++ b/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.vue @@ -63,6 +63,7 @@ }">
+
+ {{ $t('licensing.stateLicenseNumber') }}: + {{ item.licenseNumber }} + +
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..e705fde4e 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,7 +97,7 @@
{{expiresTitle}}
{{expiresContent}}
-
+
{{$t('licensing.privilegeNumSymbol')}}
{{privilegeId}}
@@ -107,6 +107,7 @@
-
+
{{ $t('licensing.privilegeId') }}
{{ privilegeId }}
@@ -437,7 +438,7 @@
-
+
{{ $t('licensing.privilegeId') }}
{{ privilegeId }}
diff --git a/webroot/src/locales/en.json b/webroot/src/locales/en.json index e39b690fb..94a7f615d 100644 --- a/webroot/src/locales/en.json +++ b/webroot/src/locales/en.json @@ -600,6 +600,7 @@ "homeState": "Home state", "stateOfHomeLicense": "State of home license", "licenseNumber": "License number", + "stateLicenseNumber": "State license number", "npi": "NPI", "searchPlaceholderNpi": "Enter NPI", "licenseExpirationDate": "License expiration date", diff --git a/webroot/src/locales/es.json b/webroot/src/locales/es.json index 183831ff7..32ee75b02 100644 --- a/webroot/src/locales/es.json +++ b/webroot/src/locales/es.json @@ -621,6 +621,7 @@ "lastUpdate": "Última carga", "license": "Licencia", "licenseNumber": "Número de licencia", + "stateLicenseNumber": "Número de licencia estatal", "npi": "NPI", "searchPlaceholderNpi": "Introducir NPI", "licenseExpirationDate": "Fecha de vencimiento de la licencia", diff --git a/webroot/src/models/Licensee/Licensee.model.ts b/webroot/src/models/Licensee/Licensee.model.ts index 335ac96c1..bf637d449 100644 --- a/webroot/src/models/Licensee/Licensee.model.ts +++ b/webroot/src/models/Licensee/Licensee.model.ts @@ -69,7 +69,7 @@ export class Licensee implements InterfaceLicensee { public $features?: StatsigClient | null = null; public id? = null; public npi? = null; - public licenseNumber?= null; + public licenseNumber? = null; public firstName? = null; public middleName? = null; public lastName? = null; diff --git a/webroot/src/network/mocks/mock.data.ts b/webroot/src/network/mocks/mock.data.ts index c8875a2ca..abfc68c71 100644 --- a/webroot/src/network/mocks/mock.data.ts +++ b/webroot/src/network/mocks/mock.data.ts @@ -185,6 +185,46 @@ export const staffAccount = { }, }, ky: { + actions: { + admin: false, + write: true, + readPrivate: true, + readSsn: true, + }, + }, + ne: { + actions: { + admin: false, + write: true, + readPrivate: true, + readSsn: true, + }, + }, + oh: { + actions: { + admin: true, + write: true, + readPrivate: true, + readSsn: true, + }, + }, + nv: { + actions: { + admin: true, + write: true, + readPrivate: true, + readSsn: true, + }, + }, + ma: { + actions: { + admin: true, + write: true, + readPrivate: true, + readSsn: true, + }, + }, + wy: { actions: { admin: true, write: true, From 71d41b28850245a76d43f3b3fce094de936cf337 Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Thu, 26 Feb 2026 10:30:10 -0700 Subject: [PATCH 03/15] WIP: Cosmo search updates --- webroot/src/pages/LicensingDetail/LicensingDetail.ts | 8 ++++++++ webroot/src/pages/LicensingDetail/LicensingDetail.vue | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/webroot/src/pages/LicensingDetail/LicensingDetail.ts b/webroot/src/pages/LicensingDetail/LicensingDetail.ts index c486f7a40..6f8df2f08 100644 --- a/webroot/src/pages/LicensingDetail/LicensingDetail.ts +++ b/webroot/src/pages/LicensingDetail/LicensingDetail.ts @@ -60,6 +60,14 @@ export default class LicensingDetail extends Vue { // // Computed // + get isAppModeJcc(): boolean { + return this.$store.getters.isAppModeJcc; + } + + get isAppModeCosmetology(): boolean { + return this.$store.getters.isAppModeCosmetology; + } + get compact(): string { const defaultCompactType = this.$store.state.user.currentCompact?.type; diff --git a/webroot/src/pages/LicensingDetail/LicensingDetail.vue b/webroot/src/pages/LicensingDetail/LicensingDetail.vue index 75789b209..90507262b 100644 --- a/webroot/src/pages/LicensingDetail/LicensingDetail.vue +++ b/webroot/src/pages/LicensingDetail/LicensingDetail.vue @@ -127,7 +127,7 @@
- +
From 3ba6cca30629acaef48f1976b71c4422169ff0e9 Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Thu, 26 Feb 2026 12:25:00 -0700 Subject: [PATCH 04/15] WIP: Cosmo search updates --- webroot/.eslintrc.js | 1 + webroot/src/models/License/License.model.ts | 2 + .../LicensingDetail/LicensingDetail.less | 4 ++ .../pages/LicensingDetail/LicensingDetail.ts | 52 ++++++++++++++++++- .../pages/LicensingDetail/LicensingDetail.vue | 19 ++++--- 5 files changed, 69 insertions(+), 9 deletions(-) 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/models/License/License.model.ts b/webroot/src/models/License/License.model.ts index 2f9db1db4..b9fe4d8a8 100644 --- a/webroot/src/models/License/License.model.ts +++ b/webroot/src/models/License/License.model.ts @@ -30,6 +30,8 @@ export enum LicenseType { // Temp server definition until server returns via end MENTAL_HEALTH_COUNSELOR = 'licensed mental health counselor', CLINICAL_MENTAL_HEALTH_COUNSELOR = 'licensed clinical mental health counselor', PROFESSIONAL_CLINICAL_COUNSELOR = 'licensed professional clinical counselor', + COSMOTOLOGIST = 'cosmetologist', + ESTHETICIAN = 'esthetician', } export enum LicenseStatus { // Temp server definition until server returns via endpoint diff --git a/webroot/src/pages/LicensingDetail/LicensingDetail.less b/webroot/src/pages/LicensingDetail/LicensingDetail.less index 6f638d3ac..957efa84f 100644 --- a/webroot/src/pages/LicensingDetail/LicensingDetail.less +++ b/webroot/src/pages/LicensingDetail/LicensingDetail.less @@ -278,6 +278,10 @@ @media @tabletWidth { flex-direction: row; flex-wrap: wrap; + + .break-wrap { + flex-basis: 100%; + } } } } diff --git a/webroot/src/pages/LicensingDetail/LicensingDetail.ts b/webroot/src/pages/LicensingDetail/LicensingDetail.ts index ba289e397..42e6815eb 100644 --- a/webroot/src/pages/LicensingDetail/LicensingDetail.ts +++ b/webroot/src/pages/LicensingDetail/LicensingDetail.ts @@ -144,7 +144,7 @@ export default class LicensingDetail extends Vue { } get licenseeLicenses(): Array { - return this.licensee?.licenses || []; + return (this.licensee?.licenses || []).slice().sort(this.sortLicenses); } get isLoading(): boolean { @@ -156,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 { @@ -270,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 007a3e42e..1c02d9622 100644 --- a/webroot/src/pages/LicensingDetail/LicensingDetail.vue +++ b/webroot/src/pages/LicensingDetail/LicensingDetail.vue @@ -189,13 +189,18 @@ />
- +
From 76fe73eb3c1826352ce4400755a1d63edc577433 Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Tue, 3 Mar 2026 10:35:06 -0700 Subject: [PATCH 05/15] WIP: Cosmo search updates --- .../LicenseeSearchLegacy/LicenseeSearchLegacy.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts index a2cb1cb45..4be52054e 100644 --- a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts +++ b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts @@ -189,6 +189,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 }); } } @@ -220,15 +221,18 @@ 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(); From 8077130de019862211e757774c040d6e39b96ac8 Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Tue, 3 Mar 2026 11:04:34 -0700 Subject: [PATCH 06/15] WIP: Cosmo search updates --- webroot/src/models/License/License.model.ts | 2 +- webroot/src/network/mocks/mock.data.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/webroot/src/models/License/License.model.ts b/webroot/src/models/License/License.model.ts index b9fe4d8a8..fa757fbac 100644 --- a/webroot/src/models/License/License.model.ts +++ b/webroot/src/models/License/License.model.ts @@ -30,7 +30,7 @@ export enum LicenseType { // Temp server definition until server returns via end MENTAL_HEALTH_COUNSELOR = 'licensed mental health counselor', CLINICAL_MENTAL_HEALTH_COUNSELOR = 'licensed clinical mental health counselor', PROFESSIONAL_CLINICAL_COUNSELOR = 'licensed professional clinical counselor', - COSMOTOLOGIST = 'cosmetologist', + COSMETOLOGIST = 'cosmetologist', ESTHETICIAN = 'esthetician', } diff --git a/webroot/src/network/mocks/mock.data.ts b/webroot/src/network/mocks/mock.data.ts index abdb84ad8..3f37adb51 100644 --- a/webroot/src/network/mocks/mock.data.ts +++ b/webroot/src/network/mocks/mock.data.ts @@ -189,7 +189,7 @@ export const staffAccount = { admin: false, write: true, readPrivate: true, - readSsn: true, + readSSN: true, }, }, ne: { @@ -197,7 +197,7 @@ export const staffAccount = { admin: false, write: true, readPrivate: true, - readSsn: true, + readSSN: true, }, }, oh: { @@ -205,7 +205,7 @@ export const staffAccount = { admin: true, write: true, readPrivate: true, - readSsn: true, + readSSN: true, }, }, nv: { @@ -213,7 +213,7 @@ export const staffAccount = { admin: true, write: true, readPrivate: true, - readSsn: true, + readSSN: true, }, }, ma: { @@ -221,7 +221,7 @@ export const staffAccount = { admin: true, write: true, readPrivate: true, - readSsn: true, + readSSN: true, }, }, wy: { From b0fb88f4554ba7382305a0b0fb13552f117efc8a Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Thu, 5 Mar 2026 12:48:42 -0700 Subject: [PATCH 07/15] WIP: Cosmo search updates --- .../Licensee/LicenseeList/LicenseeList.ts | 9 ++- .../LicenseeListLegacy/LicenseeListLegacy.ts | 57 ++++++------------- .../LicenseeSearchLegacy.less | 7 +++ .../LicenseeSearchLegacy.ts | 50 +++++++++++++++- .../LicenseeSearchLegacy.vue | 15 +++++ 5 files changed, 95 insertions(+), 43 deletions(-) diff --git a/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts b/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts index 9b8cf1478..8e5e316ca 100644 --- a/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts +++ b/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts @@ -197,6 +197,12 @@ 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, @@ -207,7 +213,8 @@ class LicenseeList extends Vue { 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 diff --git a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts index 17e141635..5afabbbbc 100644 --- a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts +++ b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts @@ -105,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(); + const joined = [ + this.searchDisplayCompact, + this.searchDisplayFullName, + this.searchDisplayState, + this.searchDisplayLicenseNumber + ].join(', ').trim(); + + return joined.replace(/(^[,\s]+)|([,\s]+$)/g, '').replace(/(,\s)\1+/g, ', '); // Replace repeated commas with single comma } get sortOptions(): Array { diff --git a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.less b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.less index 2926b4bef..a19efd662 100644 --- a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.less +++ b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.less @@ -70,4 +70,11 @@ .search-submit { margin-top: 2.4rem; } + + .clear-form { + display: inline-block; + margin-bottom: 1.2rem; + font-style: italic; + cursor: pointer; + } } diff --git a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts index 4be52054e..07280ad96 100644 --- a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts +++ b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts @@ -12,13 +12,14 @@ import { Watch, toNative } from 'vue-facing-decorator'; -import { reactive, computed } from 'vue'; +import { reactive, computed, nextTick } from 'vue'; import { AppModes } from '@/app.config'; import MixinForm from '@components/Forms/_mixins/form.mixin'; import InputText from '@components/Forms/InputText/InputText.vue'; import InputSelect from '@components/Forms/InputSelect/InputSelect.vue'; import InputSubmit from '@components/Forms/InputSubmit/InputSubmit.vue'; import SearchIcon from '@components/Icons/LicenseSearchAlt/LicenseSearchAlt.vue'; +import MockPopulate from '@components/Forms/MockPopulate/MockPopulate.vue'; import { CompactType, CompactSerializer } from '@models/Compact/Compact.model'; import { State } from '@models/State/State.model'; import { FormInput } from '@models/FormInput/FormInput.model'; @@ -39,6 +40,7 @@ export interface LicenseSearchLegacy { InputSelect, InputSubmit, SearchIcon, + MockPopulate, }, emits: [ 'searchParams' ], }) @@ -123,6 +125,10 @@ class LicenseeSearch extends mixins(MixinForm) { return compactMemberStates; } + get isMockPopulateEnabled(): boolean { + return Boolean(this.$envConfig.isDevelopment); + } + // // Methods // @@ -202,12 +208,14 @@ class LicenseeSearch extends mixins(MixinForm) { const searchProps: LicenseSearchLegacy = {}; const allowedSearchProps = [ + // Common search props 'compact', 'firstName', 'lastName', 'state' ]; + // Per compact search props if (this.appMode === AppModes.COSMETOLOGY) { allowedSearchProps.push('licenseNumber'); } @@ -238,6 +246,46 @@ class LicenseeSearch extends mixins(MixinForm) { this.checkValidForAll(); } + resetForm(): void { + if (this.enableCompactSelect) { + this.formData.compact.value = ''; + } + + 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 51f9fb553..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') }}
+
+ +
+
Date: Thu, 5 Mar 2026 13:07:30 -0700 Subject: [PATCH 08/15] WIP: Cosmo search updates --- .../Licensee/LicenseeListLegacy/LicenseeListLegacy.ts | 10 +++++----- .../LicenseeSearchLegacy/LicenseeSearchLegacy.ts | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts index 5afabbbbc..363a6cec4 100644 --- a/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts +++ b/webroot/src/components/Licensee/LicenseeListLegacy/LicenseeListLegacy.ts @@ -124,14 +124,14 @@ class LicenseeList extends Vue { } get searchDisplayAll(): string { - const joined = [ + return [ this.searchDisplayCompact, this.searchDisplayFullName, this.searchDisplayState, this.searchDisplayLicenseNumber - ].join(', ').trim(); - - return joined.replace(/(^[,\s]+)|([,\s]+$)/g, '').replace(/(,\s)\1+/g, ', '); // Replace repeated commas with single comma + ] + .filter((displayPart) => !!displayPart?.trim()) + .join(', ').trim(); } get sortOptions(): Array { @@ -296,7 +296,7 @@ class LicenseeList extends Vue { if (searchParams?.state) { requestConfig.jurisdiction = searchParams.state.toLowerCase(); } - if (searchParams?.licenseNumber) { + if (this.isAppModeCosmetology && searchParams?.licenseNumber) { requestConfig.licenseNumber = searchParams.licenseNumber; } diff --git a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts index 07280ad96..07d189d37 100644 --- a/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts +++ b/webroot/src/components/Licensee/LicenseeSearchLegacy/LicenseeSearchLegacy.ts @@ -246,9 +246,10 @@ class LicenseeSearch extends mixins(MixinForm) { this.checkValidForAll(); } - resetForm(): void { + async resetForm(): Promise { if (this.enableCompactSelect) { this.formData.compact.value = ''; + await this.$store.dispatch('user/setCurrentCompact', null); } this.formData.firstName.value = ''; From a193adcf4532c50b3ddc13b26d6b08fe2b0467e8 Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Thu, 5 Mar 2026 13:17:33 -0700 Subject: [PATCH 09/15] WIP: Cosmo search updates --- webroot/src/network/licenseApi/data.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webroot/src/network/licenseApi/data.api.ts b/webroot/src/network/licenseApi/data.api.ts index a74abfd69..f49aa10a5 100644 --- a/webroot/src/network/licenseApi/data.api.ts +++ b/webroot/src/network/licenseApi/data.api.ts @@ -124,7 +124,7 @@ export class LicenseDataApi implements DataApiInterface { sortBy, sortDirection, } = params; - const hasSearchTerms = Boolean(licenseeId || licenseeFirstName || licenseeLastName); + const hasSearchTerms = Boolean(licenseeId || licenseeFirstName || licenseeLastName || licenseNumber); const requestParams: RequestParamsInterfaceRemote = { query: {}}; if (jurisdiction) { From 05fff36c6ffb685adb4b3dcdf0b64dffdb0aa6d1 Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Thu, 19 Mar 2026 10:55:59 -0600 Subject: [PATCH 10/15] WIP: Cosmo search updates --- .../Licensee/LicenseeList/LicenseeList.ts | 10 ++++++++++ .../Licensee/LicenseeRow/LicenseeRow.vue | 3 ++- .../Licensee/LicenseeSearch/LicenseeSearch.ts | 11 +++++++++++ .../Licensee/LicenseeSearch/LicenseeSearch.vue | 11 +++++++++++ webroot/src/network/searchApi/data.api.ts | 18 ++++++++++++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts b/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts index 8e5e316ca..7c6154ce0 100644 --- a/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts +++ b/webroot/src/components/Licensee/LicenseeList/LicenseeList.ts @@ -111,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; @@ -207,6 +213,7 @@ class LicenseeList extends Vue { const joined = [ this.searchDisplayCompact, this.searchDisplayFullName, + this.searchDisplayDob, this.searchDisplayHomeState, this.searchDisplayPrivilegeState, this.searchDisplayPrivilegePurchaseDates, @@ -365,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(); } diff --git a/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.vue b/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.vue index c760ef69b..7110c6fa4 100644 --- a/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.vue +++ b/webroot/src/components/Licensee/LicenseeRow/LicenseeRow.vue @@ -89,7 +89,8 @@ :role="(isHeaderRow) ? 'columnheader' : 'cell'" > {{ $t('licensing.stateLicenseNumber') }}: - {{ item.licenseNumber }} + +
+
+ +
Date: Tue, 31 Mar 2026 10:05:33 -0600 Subject: [PATCH 11/15] WIP: Cosmo search updates --- webroot/src/components/PrivilegeCard/PrivilegeCard.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webroot/src/components/PrivilegeCard/PrivilegeCard.vue b/webroot/src/components/PrivilegeCard/PrivilegeCard.vue index e705fde4e..a08111c70 100644 --- a/webroot/src/components/PrivilegeCard/PrivilegeCard.vue +++ b/webroot/src/components/PrivilegeCard/PrivilegeCard.vue @@ -101,7 +101,7 @@
{{$t('licensing.privilegeNumSymbol')}}
{{privilegeId}}
-
+
{{ $t('licensing.disciplineStatus') }}
{{disciplineContent}}
From 73129386b3c04cfdab66c0af1de130d51d8aec17 Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Mon, 6 Apr 2026 10:48:42 -0600 Subject: [PATCH 12/15] WIP: Cosmo search updates --- webroot/src/components/Forms/InputDate/InputDate.ts | 13 ++++++++++++- webroot/src/components/Forms/_mixins/form.mixin.ts | 4 ++++ .../Licensee/LicenseeSearch/LicenseeSearch.ts | 6 +++++- webroot/src/locales/en.json | 1 + webroot/src/locales/es.json | 1 + 5 files changed, 23 insertions(+), 2 deletions(-) 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/LicenseeSearch/LicenseeSearch.ts b/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.ts index 47d5e8ad9..d58d70cfd 100644 --- a/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.ts +++ b/webroot/src/components/Licensee/LicenseeSearch/LicenseeSearch.ts @@ -18,7 +18,7 @@ import { ComputedRef, nextTick } from 'vue'; -import { AppModes } from '@/app.config'; +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'; @@ -269,6 +269,10 @@ class LicenseeSearch extends mixins(MixinForm) { 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({ diff --git a/webroot/src/locales/en.json b/webroot/src/locales/en.json index faf7a2e09..70c578ca9 100644 --- a/webroot/src/locales/en.json +++ b/webroot/src/locales/en.json @@ -463,6 +463,7 @@ "currencyType": "Must be a valid currency amount", "minNumber": "Must be greater than or equal to {min}", "maxNumber": "Must be less than or equal to {max}", + "invalidDate": "Invalid date", "invalidDateWithFormat": "Date must be of required format {format}" }, "serverErrors": { diff --git a/webroot/src/locales/es.json b/webroot/src/locales/es.json index abefa4303..57c9d16f4 100644 --- a/webroot/src/locales/es.json +++ b/webroot/src/locales/es.json @@ -447,6 +447,7 @@ "currencyType": "Debe ser un monto en moneda válido", "minNumber": "Debe ser mayor o igual a {min}", "maxNumber": "Debe ser menor o igual a {max}", + "invalidDate": "Fecha no válida", "invalidDateWithFormat": "Fecha debe estar en el formato requerido {format}" }, "serverErrors": { From cb6389351ba2f2214a9d1f31c35a1cfae5c92dbc Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Tue, 7 Apr 2026 10:19:21 -0600 Subject: [PATCH 13/15] PR review feedback --- .../LicenseeListLegacy/LicenseeListLegacy.less | 3 ++- .../LicenseeListLegacy/LicenseeListLegacy.vue | 15 ++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) 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.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')}}
From 76c243fff23235fc97879ecdc1fc17797fa1e835 Mon Sep 17 00:00:00 2001 From: John Sandoval Date: Tue, 7 Apr 2026 11:03:10 -0600 Subject: [PATCH 14/15] PR review feedback --- .../Licensee/LicenseeList/LicenseeList.less | 3 ++- .../Licensee/LicenseeList/LicenseeList.vue | 15 ++++++++------- .../components/PrivilegeCard/PrivilegeCard.vue | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) 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.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/PrivilegeCard/PrivilegeCard.vue b/webroot/src/components/PrivilegeCard/PrivilegeCard.vue index a08111c70..529035a05 100644 --- a/webroot/src/components/PrivilegeCard/PrivilegeCard.vue +++ b/webroot/src/components/PrivilegeCard/PrivilegeCard.vue @@ -270,7 +270,7 @@

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

{{ licenseeName }}
-
{{ privilegeId }}
+
{{ privilegeId }}
{{ $t('licensing.confirmPrivilegeInvestigationStartSuccess') }}
{{ licenseeName }}
-
{{ privilegeId }}
+
{{ privilegeId }}
{{ $t('licensing.confirmPrivilegeInvestigationEndSuccess') }}
{{ licenseeName }}
-
{{ privilegeId }}
+
{{ privilegeId }}
Date: Tue, 7 Apr 2026 11:44:01 -0600 Subject: [PATCH 15/15] PR review feedback --- webroot/src/components/Lists/Pagination/Pagination.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webroot/src/components/Lists/Pagination/Pagination.vue b/webroot/src/components/Lists/Pagination/Pagination.vue index 528c2ed98..f4d76b5a6 100644 --- a/webroot/src/components/Lists/Pagination/Pagination.vue +++ b/webroot/src/components/Lists/Pagination/Pagination.vue @@ -11,6 +11,7 @@