Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions webroot/src/components/Icons/AlertCircle/AlertCircle.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//
// AlertCircle.less
// CompactConnect
//
// Created by InspiringApps on 5/7/2026.
//
19 changes: 19 additions & 0 deletions webroot/src/components/Icons/AlertCircle/AlertCircle.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// AlertCircle.spec.ts
// CompactConnect
//
// Created by InspiringApps on 5/7/2026.
//

import { expect } from 'chai';
import { mountShallow } from '@tests/helpers/setup';
import AlertCircle from '@components/Icons/AlertCircle/AlertCircle.vue';

describe('AlertCircle component', async () => {
it('should mount the component', async () => {
const wrapper = await mountShallow(AlertCircle);

expect(wrapper.exists()).to.equal(true);
expect(wrapper.findComponent(AlertCircle).exists()).to.equal(true);
});
});
18 changes: 18 additions & 0 deletions webroot/src/components/Icons/AlertCircle/AlertCircle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// AlertCircle.ts
// CompactConnect
//
// Created by InspiringApps on 5/7/2026.
//

import { Component, Vue, toNative } from 'vue-facing-decorator';

@Component({
name: 'AlertCircle',
})
class AlertCircle extends Vue {
}

export default toNative(AlertCircle);

// export default AlertCircle;
23 changes: 23 additions & 0 deletions webroot/src/components/Icons/AlertCircle/AlertCircle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!--
AlertCircle.vue
CompactConnect

Created by InspiringApps on 5/7/2026.
-->

<template>
<svg
viewBox="0 0 29 29"
class="alert-circle-icon stroke-fill-type"
fill="none"
role="presentation"
aria-hidden="true"
>
<circle class="custom-fill" cx="14.5" cy="20.2626" r="1.5" />
<rect class="custom-fill" x="13" y="7.30139" width="3" height="9.46112" rx="1.5" />
<circle cx="14.5" cy="14.5" r="12.8579" stroke-width="2.5"/>
</svg>
</template>

<script lang="ts" src="./AlertCircle.ts"></script>
<style scoped lang="less" src="./AlertCircle.less"></style>
1 change: 1 addition & 0 deletions webroot/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@
"iUnderstand": "I understand",
"jurisprudenceConfirmation": "Jurisprudence Confirmation",
"lastUpdate": "Latest upload",
"disciplineTitle": "Disciplinary information",
"disciplineStatus": "Discipline status",
"noDiscipline": "No discipline",
"twoHomeStateErrorMessage": "Two states have you listed as a resident. Please update your information with those states to reflect your current residency status.",
Expand Down
1 change: 1 addition & 0 deletions webroot/src/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@
"adminFee": "Tarifa Administrativa",
"jurisdictionFee": "Tarifa Estatal",
"jurisdictionFeeMilitary": "Tarifa Estatal (militar)",
"disciplineTitle": "Información disciplinaria",
"disciplineStatus": "Estado de disciplina",
"jurisprudenceExplanationText": "Certifico que he completado con éxito un examen de jurisprudencia para este estado, si es necesario.",
"scopeAttestTitle": "Alcance de la certificación de práctica",
Expand Down
6 changes: 6 additions & 0 deletions webroot/src/models/AdverseAction/AdverseAction.model.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ describe('AdverseAction model', () => {
expect(adverseAction.endDateDisplay()).to.equal('');
expect(adverseAction.hasEndDate()).to.equal(false);
expect(adverseAction.encumbranceTypeName()).to.equal('');
expect(adverseAction.npdbTypeName()).to.equal('');
expect(adverseAction.npdbTypeNames()).to.matchPattern([]);
expect(adverseAction.isActive()).to.equal(false);
});
it('should create an AdverseAction model with specific values', () => {
Expand Down Expand Up @@ -95,6 +97,8 @@ describe('AdverseAction model', () => {
expect(adverseAction.endDateDisplay()).to.equal('Invalid date');
expect(adverseAction.hasEndDate()).to.equal(true);
expect(adverseAction.encumbranceTypeName()).to.equal('');
expect(adverseAction.npdbTypeName()).to.equal('');
expect(adverseAction.npdbTypeNames()).to.matchPattern([]);
expect(adverseAction.isActive()).to.equal(false);
});
it('should create an AdverseAction model with specific values (startDate but no endDate)', () => {
Expand Down Expand Up @@ -178,6 +182,8 @@ describe('AdverseAction model', () => {
);
expect(adverseAction.hasEndDate()).to.equal(true);
expect(adverseAction.encumbranceTypeName()).to.equal('Fine');
expect(adverseAction.npdbTypeName('Non-compliance With Requirements')).to.equal('Non-compliance With Requirements');
expect(adverseAction.npdbTypeNames()).to.matchPattern(['Non-compliance With Requirements']);
expect(adverseAction.isActive()).to.equal(true);
});
it('should create an AdverseAction model with specific values through serializer (invalid data type from server)', () => {
Expand Down
13 changes: 13 additions & 0 deletions webroot/src/models/AdverseAction/AdverseAction.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,19 @@ export class AdverseAction implements InterfaceAdverseActionCreate {
return typeName;
}

public npdbTypeName(npdbTypeKey: string): string {
const npdbTypes = this.$tm('licensing.npdbTypes') || [];
const npdbType = npdbTypes.find((translate) => translate.key === npdbTypeKey);
const typeName = npdbType?.name || '';

return typeName;
}

public npdbTypeNames(): Array<string> {
return this.npdbTypes?.map((npdbTypeKey) => this.npdbTypeName(npdbTypeKey))
.filter((npdbTypeName) => npdbTypeName.length) || [];
}

public isActive(): boolean {
// Determine whether the adverse action is currently in effect
const { startDate, endDate } = this;
Expand Down
28 changes: 27 additions & 1 deletion webroot/src/models/Licensee/Licensee.model.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import chai, { expect } from 'chai';
import chaiMatchPattern from 'chai-match-pattern';
import { serverDateFormat, displayDateFormat } from '@/app.config';
import { serverDateFormat, serverDatetimeFormat, displayDateFormat } from '@/app.config';
import { CompactType } from '@models/Compact/Compact.model';
import { Licensee, LicenseeStatus, LicenseeSerializer } from '@models/Licensee/Licensee.model';
import { Address } from '@models/Address/Address.model';
Expand All @@ -18,6 +18,7 @@ import {
EligibilityStatus
} from '@models/License/License.model';
import { MilitaryAffiliation } from '@models/MilitaryAffiliation/MilitaryAffiliation.model';
import { AdverseAction } from '@models/AdverseAction/AdverseAction.model';
import { Investigation } from '@models/Investigation/Investigation.model';
import { State } from '@models/State/State.model';
import i18n from '@/i18n';
Expand Down Expand Up @@ -60,6 +61,7 @@ describe('Licensee model', () => {
expect(licensee.licenses).to.matchPattern([]);
expect(licensee.privilegeStates).to.matchPattern([]);
expect(licensee.privileges).to.matchPattern([]);
expect(licensee.adverseActions).to.matchPattern([]);
expect(licensee.militaryAffiliations).to.matchPattern([]);
expect(licensee.militaryStatus).to.equal(null);
expect(licensee.militaryStatusNote).to.equal(null);
Expand Down Expand Up @@ -151,6 +153,7 @@ describe('Licensee model', () => {
privileges: [
new License(),
],
adverseActions: [new AdverseAction()],
lastUpdated: '2020-01-01',
status: LicenseeStatus.ACTIVE,
};
Expand Down Expand Up @@ -180,6 +183,8 @@ describe('Licensee model', () => {
expect(licensee.privilegeStates[0]).to.be.an.instanceof(State);
expect(licensee.privileges).to.be.an('array').with.length(1);
expect(licensee.privileges[0]).to.be.an.instanceof(License);
expect(licensee.adverseActions).to.be.an('array').with.length(1);
expect(licensee.adverseActions[0]).to.be.an.instanceof(AdverseAction);
expect(licensee.militaryStatus).to.equal(data.militaryStatus);
expect(licensee.militaryStatusNote).to.equal(data.militaryStatusNote);
expect(licensee.lastUpdated).to.equal(data.lastUpdated);
Expand Down Expand Up @@ -424,6 +429,25 @@ describe('Licensee model', () => {
],
dateOfUpdate: moment().format(serverDateFormat),
licenseStatus: LicenseeStatus.ACTIVE,
adverseActions: [
{
adverseActionId: 'test-adverseAction-id',
providerId: 'test-provider-id',
compact: 'aslp',
type: 'adverseAction',
encumbranceType: 'fine',
clinicalPrivilegeActionCategories: ['Non-compliance With Requirements'],
actionAgainst: 'privilege',
jurisdiction: 'oh',
licenseTypeAbbreviation: 'aud',
licenseType: 'audiologist',
creationDate: moment().subtract(1, 'week').format(serverDatetimeFormat),
effectiveStartDate: moment().subtract(1, 'month').format(serverDateFormat),
effectiveLiftDate: moment().add(11, 'months').format(serverDateFormat),
submittingUser: '1',
liftingUser: '1',
},
],
};
const licensee = LicenseeSerializer.fromServer(data);

Expand All @@ -447,6 +471,8 @@ describe('Licensee model', () => {
expect(licensee.privilegeStates[0]).to.be.an.instanceof(State);
expect(licensee.privileges).to.be.an('array').with.length(1);
expect(licensee.privileges[0]).to.be.an.instanceof(License);
expect(licensee.adverseActions).to.be.an('array').with.length(1);
expect(licensee.adverseActions[0]).to.be.an.instanceof(AdverseAction);
expect(licensee.lastUpdated).to.equal(data.dateOfUpdate);
expect(licensee.militaryAffiliations).to.be.an('array').with.length(2);
expect(licensee.militaryAffiliations[0]).to.be.an.instanceof(MilitaryAffiliation);
Expand Down
11 changes: 11 additions & 0 deletions webroot/src/models/Licensee/Licensee.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
EligibilityStatus
} from '@models/License/License.model';
import { MilitaryAffiliation, MilitaryAffiliationSerializer } from '@models/MilitaryAffiliation/MilitaryAffiliation.model';
import { AdverseAction, AdverseActionSerializer } from '@models/AdverseAction/AdverseAction.model';
import { Investigation } from '@models/Investigation/Investigation.model';
import { State } from '@models/State/State.model';
import moment from 'moment';
Expand Down Expand Up @@ -57,6 +58,7 @@ export interface InterfaceLicensee {
licenses?: Array<License>;
privilegeStates?: Array<State>;
privileges?: Array<License>;
adverseActions?: Array<AdverseAction>;
lastUpdated?: string | null;
status?: LicenseeStatus;
}
Expand Down Expand Up @@ -88,6 +90,7 @@ export class Licensee implements InterfaceLicensee {
public militaryStatus? = null;
public militaryStatusNote? = null;
public privileges? = [];
public adverseActions? = [];
public lastUpdated? = null;
public status? = LicenseeStatus.INACTIVE;

Expand Down Expand Up @@ -436,6 +439,7 @@ export class LicenseeSerializer {
licenses: [] as Array<License>,
privilegeStates: [] as Array<State>,
privileges: [] as Array<License>,
adverseActions: [] as Array<AdverseAction>,
militaryAffiliations: [] as Array<MilitaryAffiliation>,
militaryStatus: json.militaryStatus,
militaryStatusNote: json.militaryStatusNote,
Expand Down Expand Up @@ -476,6 +480,13 @@ export class LicenseeSerializer {
});
}

// In get-one responses, server returns adverse action ojects at top level for some compacts, does not in get-all
if (Array.isArray(json.adverseActions)) {
json.adverseActions.forEach((serverAdverseAction) => {
licenseeData.adverseActions.push(AdverseActionSerializer.fromServer(serverAdverseAction));
});
}

return new Licensee(licenseeData);
}

Expand Down
5 changes: 3 additions & 2 deletions webroot/src/models/StaffUser/StaffUser.model.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ describe('Staff User model', () => {
{
state: new State({ abbrev: 'co' }),
isReadPrivate: true,
isReadSsn: true,
isWrite: true,
isAdmin: true,
},
Expand All @@ -166,9 +167,9 @@ describe('Staff User model', () => {
const user = new StaffUser(data);

expect(user).to.be.an.instanceof(StaffUser);
expect(user.permissionsShortDisplay(CompactType.ASLP)).to.equal('Read Private, Write, Admin');
expect(user.permissionsShortDisplay(CompactType.ASLP)).to.equal('Read Private, Read SSN, Write, Admin');
expect(user.permissionsFullDisplay()).to.matchPattern([
'Colorado: Read Private, Write, Admin',
'Colorado: Read Private, Read SSN, Write, Admin',
]);
expect(user.affiliationDisplay(CompactType.ASLP)).to.equal('Colorado');
expect(user.statesDisplay(CompactType.ASLP)).to.equal('Colorado');
Expand Down
96 changes: 94 additions & 2 deletions webroot/src/network/mocks/mock.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ export const licensees = {
type: 'adverseAction',
encumbranceType: 'fine',
clinicalPrivilegeActionCategories: ['Non-compliance With Requirements'],
actionAgainst: 'privilege',
actionAgainst: 'license',
jurisdiction: 'nv',
licenseTypeAbbreviation: 'ota',
licenseType: 'occupational therapy assistant',
Expand All @@ -845,7 +845,7 @@ export const licensees = {
type: 'adverseAction',
encumbranceType: 'fine',
clinicalPrivilegeActionCategories: ['Non-compliance With Requirements'],
actionAgainst: 'privilege',
actionAgainst: 'license',
jurisdiction: 'nv',
licenseTypeAbbreviation: 'ota',
licenseType: 'occupational therapy assistant',
Expand Down Expand Up @@ -1083,6 +1083,98 @@ export const licensees = {
],
},
],
adverseActions: [
{
adverseActionId: '12345-ABC',
providerId: 'aa2e057d-6972-4a68-a55d-aad1c3d05278',
compact: 'octp',
type: 'adverseAction',
encumbranceType: 'fine',
clinicalPrivilegeActionCategories: [
'Non-compliance With Requirements',
'Confidentiality, Consent or Disclosure Violations',
'Improper Supervision or Allowing Unlicensed Practice',
'Consumer Harm',
],
actionAgainst: 'privilege',
jurisdiction: 'oh',
licenseTypeAbbreviation: 'ota',
licenseType: 'occupational therapy assistant',
creationDate: moment().subtract(1, 'week').format(serverDatetimeFormat),
effectiveStartDate: moment().subtract(1, 'month').format(serverDateFormat),
effectiveLiftDate: moment().add(11, 'months').format(serverDateFormat),
submittingUser: '1',
liftingUser: '1',
},
{
adverseActionId: '12345-DEF',
providerId: 'aa2e057d-6972-4a68-a55d-aad1c3d05278',
compact: 'octp',
type: 'adverseAction',
encumbranceType: 'fine',
clinicalPrivilegeActionCategories: ['Non-compliance With Requirements'],
actionAgainst: 'privilege',
jurisdiction: 'al',
licenseTypeAbbreviation: 'ota',
licenseType: 'occupational therapy assistant',
creationDate: moment().subtract(8, 'months').format(serverDatetimeFormat),
effectiveStartDate: moment().subtract(7, 'months').format(serverDateFormat),
effectiveLiftDate: moment().subtract(5, 'months').format(serverDateFormat),
submittingUser: '1',
liftingUser: '1',
},
{
adverseActionId: '12345-GHI',
providerId: 'aa2e057d-6972-4a68-a55d-aad1c3d05278',
compact: 'octp',
type: 'adverseAction',
encumbranceType: 'fine',
clinicalPrivilegeActionCategories: ['Unsafe Practice or Substandard Care'],
actionAgainst: 'privilege',
jurisdiction: 'al',
licenseTypeAbbreviation: 'ota',
licenseType: 'occupational therapy assistant',
creationDate: moment().subtract(1, 'week').format(serverDatetimeFormat),
effectiveStartDate: moment().subtract(3, 'months').format(serverDateFormat),
effectiveLiftDate: moment().subtract(1, 'months').format(serverDateFormat),
submittingUser: '1',
liftingUser: null,
},
{
adverseActionId: '12345-MNO',
providerId: 'aa2e057d-6972-4a68-a55d-aad1c3d05278',
compact: 'octp',
type: 'adverseAction',
encumbranceType: 'fine',
clinicalPrivilegeActionCategories: ['Non-compliance With Requirements'],
actionAgainst: 'license',
jurisdiction: 'nv',
licenseTypeAbbreviation: 'ota',
licenseType: 'occupational therapy assistant',
creationDate: moment().subtract(1, 'year').format(serverDatetimeFormat),
effectiveStartDate: moment().subtract(1, 'year').format(serverDateFormat),
effectiveLiftDate: moment().subtract(1, 'month').format(serverDateFormat),
submittingUser: '1',
liftingUser: '1',
},
{
adverseActionId: '12345-JKL',
providerId: 'aa2e057d-6972-4a68-a55d-aad1c3d05278',
compact: 'octp',
type: 'adverseAction',
encumbranceType: 'fine',
clinicalPrivilegeActionCategories: ['Non-compliance With Requirements'],
actionAgainst: 'license',
jurisdiction: 'nv',
licenseTypeAbbreviation: 'ota',
licenseType: 'occupational therapy assistant',
creationDate: moment().subtract(1, 'week').format(serverDatetimeFormat),
effectiveStartDate: moment().subtract(1, 'month').format(serverDateFormat),
effectiveLiftDate: null,
submittingUser: '1',
liftingUser: null,
},
],
},
{
// ================================================================
Expand Down
Loading
Loading