diff --git a/webroot/src/app.config.ts b/webroot/src/app.config.ts index fa69000e0..735143403 100644 --- a/webroot/src/app.config.ts +++ b/webroot/src/app.config.ts @@ -216,6 +216,172 @@ export const compacts = { cosm: {}, }; +export const getEncumberConfigLicense = (appMode: AppModes) => { + let disciplineTypes: Array = []; + let npdbTypes: Array = []; + + switch (appMode) { + case AppModes.JCC: + disciplineTypes = [ + 'fine', + 'reprimand', + 'required supervision', + 'completion of continuing education', + 'public reprimand', + 'probation', + 'injunctive action', + 'suspension', + 'revocation', + 'denial', + 'surrender of license', + 'modification of previous action-extension', + 'modification of previous action-reduction', + 'other monitoring', + 'other adjudicated action not listed', + ]; + npdbTypes = [ + 'Non-compliance With Requirements', + 'Criminal Conviction or Adjudication', + 'Confidentiality, Consent or Disclosure Violations', + 'Misconduct or Abuse', + 'Fraud, Deception, or Misrepresentation', + 'Unsafe Practice or Substandard Care', + 'Improper Supervision or Allowing Unlicensed Practice', + 'Other', + ]; + break; + case AppModes.COSMETOLOGY: + disciplineTypes = [ + 'suspension', + 'revocation', + 'surrender of license', + ]; + npdbTypes = [ + 'fraud', + 'consumer harm', + 'other', + ]; + break; + case AppModes.SOCIAL_WORK: + disciplineTypes = [ + 'fine', + 'reprimand', + 'required supervision', + 'completion of continuing education', + 'public reprimand', + 'probation', + 'injunctive action', + 'suspension', + 'revocation', + 'denial', + 'surrender of license', + 'modification of previous action-extension', + 'modification of previous action-reduction', + 'other monitoring', + 'other adjudicated action not listed', + ]; + npdbTypes = [ + 'Non-compliance With Requirements', + 'Conflict of Interest', + 'Substandard Care or Patient Neglect/Abuse', + 'Criminal Conviction or Adjudication', + 'Confidentiality, Consent or Disclosure Violations', + 'Fraud, Deception, or Misrepresentation', + 'Improper Supervision or Allowing Unlicensed Practice', + 'Improper Prescribing, Dispensing, Administering Medication/Drug Violation', + 'Other', + ]; + break; + default: + break; + } + + return { disciplineTypes, npdbTypes }; +}; + +export const getEncumberConfigPrivilege = (appMode: AppModes) => { + let disciplineTypes: Array = []; + let npdbTypes: Array = []; + + switch (appMode) { + case AppModes.JCC: + disciplineTypes = [ + 'fine', + 'reprimand', + 'required supervision', + 'completion of continuing education', + 'public reprimand', + 'probation', + 'injunctive action', + 'suspension', + 'revocation', + 'denial', + 'surrender of privilege', + 'modification of previous action-extension', + 'modification of previous action-reduction', + 'other monitoring', + 'other adjudicated action not listed', + ]; + npdbTypes = [ + 'Non-compliance With Requirements', + 'Criminal Conviction or Adjudication', + 'Confidentiality, Consent or Disclosure Violations', + 'Misconduct or Abuse', + 'Fraud, Deception, or Misrepresentation', + 'Unsafe Practice or Substandard Care', + 'Improper Supervision or Allowing Unlicensed Practice', + 'Other', + ]; + break; + case AppModes.COSMETOLOGY: + disciplineTypes = [ + 'suspension', + 'revocation', + 'surrender of privilege', + ]; + npdbTypes = [ + 'fraud', + 'consumer harm', + 'other', + ]; + break; + case AppModes.SOCIAL_WORK: + disciplineTypes = [ + 'fine', + 'reprimand', + 'required supervision', + 'completion of continuing education', + 'public reprimand', + 'probation', + 'injunctive action', + 'suspension', + 'revocation', + 'denial', + 'surrender of privilege', + 'modification of previous action-extension', + 'modification of previous action-reduction', + 'other monitoring', + 'other adjudicated action not listed', + ]; + npdbTypes = [ + 'Non-compliance With Requirements', + 'Conflict of Interest', + 'Substandard Care or Patient Neglect/Abuse', + 'Criminal Conviction or Adjudication', + 'Confidentiality, Consent or Disclosure Violations', + 'Fraud, Deception, or Misrepresentation', + 'Improper Supervision or Allowing Unlicensed Practice', + 'Improper Prescribing, Dispensing, Administering Medication/Drug Violation', + 'Other', + ]; + break; + default: + break; + } + + return { disciplineTypes, npdbTypes }; +}; + // ============================= // = Feature gate IDs = // ============================= diff --git a/webroot/src/components/LicenseCard/LicenseCard.spec.ts b/webroot/src/components/LicenseCard/LicenseCard.spec.ts index 95a5d2cc1..e81e5faa6 100644 --- a/webroot/src/components/LicenseCard/LicenseCard.spec.ts +++ b/webroot/src/components/LicenseCard/LicenseCard.spec.ts @@ -8,6 +8,7 @@ import { expect } from 'chai'; import { mountShallow } from '@tests/helpers/setup'; import LicenseCard from '@components/LicenseCard/LicenseCard.vue'; +import { AppModes, getEncumberConfigLicense } from '@/app.config'; describe('LicenseCard component', async () => { it('should mount the component', async () => { @@ -16,4 +17,81 @@ describe('LicenseCard component', async () => { expect(wrapper.exists()).to.equal(true); expect(wrapper.findComponent(LicenseCard).exists()).to.equal(true); }); + it('should successfully return expected encumber config for license (jcc)', async () => { + const config = getEncumberConfigLicense(AppModes.JCC); + + expect(config.disciplineTypes).to.eql([ + 'fine', + 'reprimand', + 'required supervision', + 'completion of continuing education', + 'public reprimand', + 'probation', + 'injunctive action', + 'suspension', + 'revocation', + 'denial', + 'surrender of license', + 'modification of previous action-extension', + 'modification of previous action-reduction', + 'other monitoring', + 'other adjudicated action not listed', + ]); + expect(config.npdbTypes).to.eql([ + 'Non-compliance With Requirements', + 'Criminal Conviction or Adjudication', + 'Confidentiality, Consent or Disclosure Violations', + 'Misconduct or Abuse', + 'Fraud, Deception, or Misrepresentation', + 'Unsafe Practice or Substandard Care', + 'Improper Supervision or Allowing Unlicensed Practice', + 'Other', + ]); + }); + it('should successfully return expected encumber config for license (cosmetology)', async () => { + const config = getEncumberConfigLicense(AppModes.COSMETOLOGY); + + expect(config.disciplineTypes).to.eql([ + 'suspension', + 'revocation', + 'surrender of license', + ]); + expect(config.npdbTypes).to.eql([ + 'fraud', + 'consumer harm', + 'other', + ]); + }); + it('should successfully return expected encumber config for license (social-work)', async () => { + const config = getEncumberConfigLicense(AppModes.SOCIAL_WORK); + + expect(config.disciplineTypes).to.eql([ + 'fine', + 'reprimand', + 'required supervision', + 'completion of continuing education', + 'public reprimand', + 'probation', + 'injunctive action', + 'suspension', + 'revocation', + 'denial', + 'surrender of license', + 'modification of previous action-extension', + 'modification of previous action-reduction', + 'other monitoring', + 'other adjudicated action not listed', + ]); + expect(config.npdbTypes).to.eql([ + 'Non-compliance With Requirements', + 'Conflict of Interest', + 'Substandard Care or Patient Neglect/Abuse', + 'Criminal Conviction or Adjudication', + 'Confidentiality, Consent or Disclosure Violations', + 'Fraud, Deception, or Misrepresentation', + 'Improper Supervision or Allowing Unlicensed Practice', + 'Improper Prescribing, Dispensing, Administering Medication/Drug Violation', + 'Other', + ]); + }); }); diff --git a/webroot/src/components/LicenseCard/LicenseCard.ts b/webroot/src/components/LicenseCard/LicenseCard.ts index 8f9d6e506..f4512985a 100644 --- a/webroot/src/components/LicenseCard/LicenseCard.ts +++ b/webroot/src/components/LicenseCard/LicenseCard.ts @@ -16,7 +16,7 @@ import { ComputedRef, nextTick } from 'vue'; -import { dateFormatPatterns } from '@/app.config'; +import { dateFormatPatterns, AppModes, getEncumberConfigLicense } from '@/app.config'; import MixinForm from '@components/Forms/_mixins/form.mixin'; import InputDate from '@components/Forms/InputDate/InputDate.vue'; import InputSelect from '@components/Forms/InputSelect/InputSelect.vue'; @@ -108,6 +108,10 @@ class LicenseCard extends mixins(MixinForm) { return this.$store.state.user; } + get appMode(): AppModes { + return this.$store.state.appMode; + } + get isAppModeJcc(): boolean { return this.$store.getters.isAppModeJcc; } @@ -284,21 +288,13 @@ class LicenseCard extends mixins(MixinForm) { } get encumberDisciplineOptions(): Array<{ value: string, name: string | ComputedRef }> { - let options = this.$tm('licensing.disciplineTypes').map((disciplineType) => ({ + const includeList: Array = getEncumberConfigLicense(this.appMode).disciplineTypes; + const options = this.$tm('licensing.disciplineTypes').map((disciplineType) => ({ value: disciplineType.key, name: disciplineType.name, - })); - - if (this.isAppModeCosmetology || this.isAppModeSocialWork) { - const includeList = ['suspension', 'revocation', 'surrender of license']; - - options = options.filter((option) => includeList.includes(option.value)); - } else { - const excludeList = ['surrender of privilege']; - - options = options.filter((option) => !excludeList.includes(option.value)); - } + })).filter((option) => includeList.includes(option.value) || option.value === ''); + // For a single-select, include the blank option options.unshift({ value: '', name: computed(() => this.$t('common.selectOption')), @@ -308,41 +304,14 @@ class LicenseCard extends mixins(MixinForm) { } get npdbCategoryOptions(): Array<{ value: string, name: string | ComputedRef }> { - const { isAppModeJcc, isAppModeCosmetology, isAppModeSocialWork } = this; - const includeList: Array = []; - let isMultiSelect = true; - let options = this.$tm('licensing.npdbTypes').map((npdbType) => ({ + const includeList: Array = getEncumberConfigLicense(this.appMode).npdbTypes; + const options = this.$tm('licensing.npdbTypes').map((npdbType) => ({ value: npdbType.key, name: npdbType.name, - })); - - // Define the included keys per compact - if (isAppModeJcc) { - includeList.push('Non-compliance With Requirements'); - includeList.push('Criminal Conviction or Adjudication'); - includeList.push('Confidentiality, Consent or Disclosure Violations'); - includeList.push('Misconduct or Abuse'); - includeList.push('Fraud, Deception, or Misrepresentation'); - includeList.push('Unsafe Practice or Substandard Care'); - includeList.push('Improper Supervision or Allowing Unlicensed Practice'); - includeList.push('Other'); - } else if (isAppModeCosmetology) { - isMultiSelect = false; - includeList.push('fraud'); - includeList.push('consumer harm'); - includeList.push('other'); - } else if (isAppModeSocialWork) { - isMultiSelect = false; - includeList.push('fraud'); - includeList.push('consumer harm'); - includeList.push('other'); - } - - // Filter the compact-specific options - options = options.filter((option) => includeList.includes(option.value) || option.value === ''); + })).filter((option) => includeList.includes(option.value) || option.value === ''); // For a single-select, include the blank option - if (!isMultiSelect) { + if (!this.shouldAllowNpdbMultiSelect) { options.unshift({ value: '', name: computed(() => this.$t('common.selectOption')) }); } @@ -350,7 +319,7 @@ class LicenseCard extends mixins(MixinForm) { } get shouldAllowNpdbMultiSelect(): boolean { - return this.isAppModeJcc; + return this.isAppModeJcc || this.isAppModeSocialWork; } get endInvestigationModalTitle(): string { diff --git a/webroot/src/components/PrivilegeCard/PrivilegeCard.spec.ts b/webroot/src/components/PrivilegeCard/PrivilegeCard.spec.ts index 7a0a35c1d..18cc5e7f7 100644 --- a/webroot/src/components/PrivilegeCard/PrivilegeCard.spec.ts +++ b/webroot/src/components/PrivilegeCard/PrivilegeCard.spec.ts @@ -8,6 +8,7 @@ import { expect } from 'chai'; import { mountShallow } from '@tests/helpers/setup'; import PrivilegeCard from '@components/PrivilegeCard/PrivilegeCard.vue'; +import { AppModes, getEncumberConfigPrivilege } from '@/app.config'; describe('PrivilegeCard component', async () => { it('should mount the component', async () => { @@ -16,4 +17,81 @@ describe('PrivilegeCard component', async () => { expect(wrapper.exists()).to.equal(true); expect(wrapper.findComponent(PrivilegeCard).exists()).to.equal(true); }); + it('should successfully return expected encumber config for privilege (jcc)', async () => { + const config = getEncumberConfigPrivilege(AppModes.JCC); + + expect(config.disciplineTypes).to.eql([ + 'fine', + 'reprimand', + 'required supervision', + 'completion of continuing education', + 'public reprimand', + 'probation', + 'injunctive action', + 'suspension', + 'revocation', + 'denial', + 'surrender of privilege', + 'modification of previous action-extension', + 'modification of previous action-reduction', + 'other monitoring', + 'other adjudicated action not listed', + ]); + expect(config.npdbTypes).to.eql([ + 'Non-compliance With Requirements', + 'Criminal Conviction or Adjudication', + 'Confidentiality, Consent or Disclosure Violations', + 'Misconduct or Abuse', + 'Fraud, Deception, or Misrepresentation', + 'Unsafe Practice or Substandard Care', + 'Improper Supervision or Allowing Unlicensed Practice', + 'Other', + ]); + }); + it('should successfully return expected encumber config for privilege (cosmetology)', async () => { + const config = getEncumberConfigPrivilege(AppModes.COSMETOLOGY); + + expect(config.disciplineTypes).to.eql([ + 'suspension', + 'revocation', + 'surrender of privilege', + ]); + expect(config.npdbTypes).to.eql([ + 'fraud', + 'consumer harm', + 'other', + ]); + }); + it('should successfully return expected encumber config for privilege (social-work)', async () => { + const config = getEncumberConfigPrivilege(AppModes.SOCIAL_WORK); + + expect(config.disciplineTypes).to.eql([ + 'fine', + 'reprimand', + 'required supervision', + 'completion of continuing education', + 'public reprimand', + 'probation', + 'injunctive action', + 'suspension', + 'revocation', + 'denial', + 'surrender of privilege', + 'modification of previous action-extension', + 'modification of previous action-reduction', + 'other monitoring', + 'other adjudicated action not listed', + ]); + expect(config.npdbTypes).to.eql([ + 'Non-compliance With Requirements', + 'Conflict of Interest', + 'Substandard Care or Patient Neglect/Abuse', + 'Criminal Conviction or Adjudication', + 'Confidentiality, Consent or Disclosure Violations', + 'Fraud, Deception, or Misrepresentation', + 'Improper Supervision or Allowing Unlicensed Practice', + 'Improper Prescribing, Dispensing, Administering Medication/Drug Violation', + 'Other', + ]); + }); }); diff --git a/webroot/src/components/PrivilegeCard/PrivilegeCard.ts b/webroot/src/components/PrivilegeCard/PrivilegeCard.ts index 8c0513fee..eafb99070 100644 --- a/webroot/src/components/PrivilegeCard/PrivilegeCard.ts +++ b/webroot/src/components/PrivilegeCard/PrivilegeCard.ts @@ -16,7 +16,7 @@ import { ComputedRef, nextTick } from 'vue'; -import { dateFormatPatterns } from '@/app.config'; +import { dateFormatPatterns, AppModes, getEncumberConfigPrivilege } from '@/app.config'; import MixinForm from '@components/Forms/_mixins/form.mixin'; import InputTextarea from '@components/Forms/InputTextarea/InputTextarea.vue'; import InputDate from '@components/Forms/InputDate/InputDate.vue'; @@ -87,6 +87,10 @@ class PrivilegeCard extends mixins(MixinForm) { return this.$store.state.user; } + get appMode(): AppModes { + return this.$store.state.appMode; + } + get isAppModeJcc(): boolean { return this.$store.getters.isAppModeJcc; } @@ -235,21 +239,13 @@ class PrivilegeCard extends mixins(MixinForm) { } get encumberDisciplineOptions(): Array<{ value: string, name: string | ComputedRef }> { - let options = this.$tm('licensing.disciplineTypes').map((disciplineType) => ({ + const includeList: Array = getEncumberConfigPrivilege(this.appMode).disciplineTypes; + const options = this.$tm('licensing.disciplineTypes').map((disciplineType) => ({ value: disciplineType.key, name: disciplineType.name, - })); - - if (this.isAppModeCosmetology || this.isAppModeSocialWork) { - const includeList = ['suspension', 'revocation', 'surrender of privilege']; - - options = options.filter((option) => includeList.includes(option.value)); - } else { - const excludeList = ['surrender of license']; - - options = options.filter((option) => !excludeList.includes(option.value)); - } + })).filter((option) => includeList.includes(option.value) || option.value === ''); + // For a single-select, include the blank option options.unshift({ value: '', name: computed(() => this.$t('common.selectOption')), @@ -259,41 +255,14 @@ class PrivilegeCard extends mixins(MixinForm) { } get npdbCategoryOptions(): Array<{ value: string, name: string | ComputedRef }> { - const { isAppModeJcc, isAppModeCosmetology, isAppModeSocialWork } = this; - const includeList: Array = []; - let isMultiSelect = true; - let options = this.$tm('licensing.npdbTypes').map((npdbType) => ({ + const includeList: Array = getEncumberConfigPrivilege(this.appMode).npdbTypes; + const options = this.$tm('licensing.npdbTypes').map((npdbType) => ({ value: npdbType.key, name: npdbType.name, - })); - - // Define the included keys per compact - if (isAppModeJcc) { - includeList.push('Non-compliance With Requirements'); - includeList.push('Criminal Conviction or Adjudication'); - includeList.push('Confidentiality, Consent or Disclosure Violations'); - includeList.push('Misconduct or Abuse'); - includeList.push('Fraud, Deception, or Misrepresentation'); - includeList.push('Unsafe Practice or Substandard Care'); - includeList.push('Improper Supervision or Allowing Unlicensed Practice'); - includeList.push('Other'); - } else if (isAppModeCosmetology) { - isMultiSelect = false; - includeList.push('fraud'); - includeList.push('consumer harm'); - includeList.push('other'); - } else if (isAppModeSocialWork) { - isMultiSelect = false; - includeList.push('fraud'); - includeList.push('consumer harm'); - includeList.push('other'); - } - - // Filter the compact-specific options - options = options.filter((option) => includeList.includes(option.value) || option.value === ''); + })).filter((option) => includeList.includes(option.value) || option.value === ''); // For a single-select, include the blank option - if (!isMultiSelect) { + if (!this.shouldAllowNpdbMultiSelect) { options.unshift({ value: '', name: computed(() => this.$t('common.selectOption')) }); } @@ -301,7 +270,7 @@ class PrivilegeCard extends mixins(MixinForm) { } get shouldAllowNpdbMultiSelect(): boolean { - return this.isAppModeJcc; + return this.isAppModeJcc || this.isAppModeSocialWork; } get endInvestigationModalTitle(): string { diff --git a/webroot/src/locales/en.json b/webroot/src/locales/en.json index fbe62672a..7b12c1b5f 100644 --- a/webroot/src/locales/en.json +++ b/webroot/src/locales/en.json @@ -957,6 +957,14 @@ "name": "Non-compliance With Requirements", "key": "Non-compliance With Requirements" }, + { + "name": "Conflict of Interest", + "key": "Conflict of Interest" + }, + { + "name": "Substandard Care or Patient Neglect/Abuse", + "key": "Substandard Care or Patient Neglect/Abuse" + }, { "name": "Criminal Conviction or Adjudication", "key": "Criminal Conviction or Adjudication" @@ -981,6 +989,10 @@ "name": "Improper Supervision or Allowing Unlicensed Practice", "key": "Improper Supervision or Allowing Unlicensed Practice" }, + { + "name": "Improper Prescribing, Dispensing, Administering Medication/Drug Violation", + "key": "Improper Prescribing, Dispensing, Administering Medication/Drug Violation" + }, { "name": "Fraud", "key": "fraud" diff --git a/webroot/src/locales/es.json b/webroot/src/locales/es.json index d36da7cb3..cfd1facad 100644 --- a/webroot/src/locales/es.json +++ b/webroot/src/locales/es.json @@ -941,6 +941,14 @@ "name": "Incumplimiento de los requisitos", "key": "Non-compliance With Requirements" }, + { + "name": "Conflicto de intereses", + "key": "Conflict of Interest" + }, + { + "name": "Atención deficiente o negligencia/abuso del paciente", + "key": "Substandard Care or Patient Neglect/Abuse" + }, { "name": "Condena o sentencia penal", "key": "Criminal Conviction or Adjudication" @@ -965,6 +973,10 @@ "name": "Supervisión inadecuada o autorización de prácticas sin licencia", "key": "Improper Supervision or Allowing Unlicensed Practice" }, + { + "name": "Prescripción, dispensación y administración inadecuadas de medicamentos/violación de la ley de medicamentos", + "key": "Improper Prescribing, Dispensing, Administering Medication/Drug Violation" + }, { "name": "Fraude", "key": "fraud" diff --git a/webroot/src/pages/PublicDashboard/PublicDashboard.vue b/webroot/src/pages/PublicDashboard/PublicDashboard.vue index 22ba1ba6c..b5ce198a5 100644 --- a/webroot/src/pages/PublicDashboard/PublicDashboard.vue +++ b/webroot/src/pages/PublicDashboard/PublicDashboard.vue @@ -224,7 +224,7 @@ {{ $t('privacyPolicy.title') }}