diff --git a/webroot/src/components/CompactSettingsConfig/CompactSettingsConfig.ts b/webroot/src/components/CompactSettingsConfig/CompactSettingsConfig.ts index 830430032..e74a4f0c6 100644 --- a/webroot/src/components/CompactSettingsConfig/CompactSettingsConfig.ts +++ b/webroot/src/components/CompactSettingsConfig/CompactSettingsConfig.ts @@ -93,6 +93,12 @@ class CompactSettingsConfig extends mixins(MixinForm) { return this.userStore.model; } + get liveStatusLabel(): string { + return (this.isAppModeCosmetology) + ? this.$t('compact.licenseRegistrationEnabledSubtextCosm') + : this.$t('compact.licenseRegistrationEnabledSubtext'); + } + get submitLabel(): string { return (this.isFormLoading) ? this.$t('common.loading') : this.$t('common.saveChanges'); } @@ -189,7 +195,7 @@ class CompactSettingsConfig extends mixins(MixinForm) { id: 'registration-enabled', name: 'registration-enabled', label: computed(() => this.$t('compact.licenseRegistrationEnabled')), - labelSubtext: computed(() => this.$t('compact.licenseRegistrationEnabledSubtext')), + labelSubtext: computed(() => this.liveStatusLabel), validation: Joi.boolean().required().messages(this.joiMessages.boolean), valueOptions: [ { value: true, name: computed(() => this.$t('common.yes')) }, diff --git a/webroot/src/components/LicenseCard/LicenseCard.ts b/webroot/src/components/LicenseCard/LicenseCard.ts index a01799d22..d56f70a88 100644 --- a/webroot/src/components/LicenseCard/LicenseCard.ts +++ b/webroot/src/components/LicenseCard/LicenseCard.ts @@ -64,6 +64,7 @@ class LicenseCard extends mixins(MixinForm) { @Prop({ required: true }) licensee!: Licensee; @Prop({ default: null }) homeState?: State | null; @Prop({ default: false }) shouldIncludeLogo?: boolean; + @Prop({ default: false }) isPublicSearch!: boolean; // // Data diff --git a/webroot/src/components/LicenseCard/LicenseCard.vue b/webroot/src/components/LicenseCard/LicenseCard.vue index 501f9c5a7..71688649e 100644 --- a/webroot/src/components/LicenseCard/LicenseCard.vue +++ b/webroot/src/components/LicenseCard/LicenseCard.vue @@ -80,7 +80,13 @@
{{licenseTypeAbbrev}}
-
{{statusDescriptionDisplay}}
+
+ {{statusDescriptionDisplay}} +
@@ -91,23 +97,25 @@
{{$t('licensing.licenseNumSymbol')}}
{{licenseNumber}}
-
+
{{ $t('licensing.disciplineStatus') }}
{{disciplineContent}}
-
-
- +
-
{{ $t('licensing.licensingListDescription')}}
+
{{ listDescriptionText }}
-
+
this.$t('compact.privilegePurchaseEnabled')), - labelSubtext: computed(() => this.$t('compact.privilegePurchaseEnabledSubtext')), + labelSubtext: computed(() => this.liveStatusLabel), validation: Joi.boolean().required().messages(this.joiMessages.boolean), valueOptions: [ { value: true, name: computed(() => this.$t('common.yes')) }, diff --git a/webroot/src/components/Users/UserInvite/UserInvite.ts b/webroot/src/components/Users/UserInvite/UserInvite.ts index 1faa929db..6c66d25a1 100644 --- a/webroot/src/components/Users/UserInvite/UserInvite.ts +++ b/webroot/src/components/Users/UserInvite/UserInvite.ts @@ -78,6 +78,14 @@ class UserInvite 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; } @@ -127,12 +135,24 @@ class UserInvite extends mixins(MixinForm) { } get userPermissionOptionsCompact(): Array { - return [ - { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, - { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, - { value: Permission.READ_SSN, name: this.$t('account.accessLevel.readSsn') }, - { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, - ]; + let permissionOptions: Array = []; + + if (this.isAppModeCosmetology) { + permissionOptions = [ + { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, + { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, + { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, + ]; + } else { + permissionOptions = [ + { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, + { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, + { value: Permission.READ_SSN, name: this.$t('account.accessLevel.readSsn') }, + { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, + ]; + } + + return permissionOptions; } get currentUserStatePermissions(): Array { @@ -142,13 +162,26 @@ class UserInvite extends mixins(MixinForm) { } get userPermissionOptionsState(): Array { - return [ - { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, - { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, - { value: Permission.READ_SSN, name: this.$t('account.accessLevel.readSsn') }, - { value: Permission.WRITE, name: this.$t('account.accessLevel.write') }, - { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, - ]; + let permissionOptions: Array = []; + + if (this.isAppModeCosmetology) { + permissionOptions = [ + { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, + { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, + { value: Permission.WRITE, name: this.$t('account.accessLevel.write') }, + { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, + ]; + } else { + permissionOptions = [ + { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, + { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, + { value: Permission.READ_SSN, name: this.$t('account.accessLevel.readSsn') }, + { value: Permission.WRITE, name: this.$t('account.accessLevel.write') }, + { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, + ]; + } + + return permissionOptions; } get userOptionsState(): Array { @@ -388,6 +421,10 @@ class UserInvite extends mixins(MixinForm) { } } + if (this.isAppModeCosmetology) { + delete response.isReadSsn; + } + return response; } @@ -447,6 +484,10 @@ class UserInvite extends mixins(MixinForm) { break; } + if (this.isAppModeCosmetology) { + delete response.isReadSsn; + } + return response; } diff --git a/webroot/src/components/Users/UserRowEdit/UserRowEdit.ts b/webroot/src/components/Users/UserRowEdit/UserRowEdit.ts index ac913aa03..fa8a3171a 100644 --- a/webroot/src/components/Users/UserRowEdit/UserRowEdit.ts +++ b/webroot/src/components/Users/UserRowEdit/UserRowEdit.ts @@ -81,6 +81,14 @@ class UserRowEdit 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; } @@ -142,12 +150,24 @@ class UserRowEdit extends mixins(MixinForm) { } get userPermissionOptionsCompact(): Array { - return [ - { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, - { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, - { value: Permission.READ_SSN, name: this.$t('account.accessLevel.readSsn') }, - { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, - ]; + let permissionOptions: Array = []; + + if (this.isAppModeCosmetology) { + permissionOptions = [ + { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, + { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, + { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, + ]; + } else { + permissionOptions = [ + { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, + { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, + { value: Permission.READ_SSN, name: this.$t('account.accessLevel.readSsn') }, + { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, + ]; + } + + return permissionOptions; } get currentUserStatePermissions(): Array { @@ -161,13 +181,26 @@ class UserRowEdit extends mixins(MixinForm) { } get userPermissionOptionsState(): Array { - return [ - { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, - { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, - { value: Permission.READ_SSN, name: this.$t('account.accessLevel.readSsn') }, - { value: Permission.WRITE, name: this.$t('account.accessLevel.write') }, - { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, - ]; + let permissionOptions: Array = []; + + if (this.isAppModeCosmetology) { + permissionOptions = [ + { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, + { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, + { value: Permission.WRITE, name: this.$t('account.accessLevel.write') }, + { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, + ]; + } else { + permissionOptions = [ + { value: Permission.NONE, name: this.$t('account.accessLevel.none') }, + { value: Permission.READ_PRIVATE, name: this.$t('account.accessLevel.readPrivate') }, + { value: Permission.READ_SSN, name: this.$t('account.accessLevel.readSsn') }, + { value: Permission.WRITE, name: this.$t('account.accessLevel.write') }, + { value: Permission.ADMIN, name: this.$t('account.accessLevel.admin') }, + ]; + } + + return permissionOptions; } get userOptionsState(): Array { @@ -389,6 +422,10 @@ class UserRowEdit extends mixins(MixinForm) { } } + if (this.isAppModeCosmetology) { + delete response.isReadSsn; + } + return response; } @@ -448,6 +485,10 @@ class UserRowEdit extends mixins(MixinForm) { break; } + if (this.isAppModeCosmetology) { + delete response.isReadSsn; + } + return response; } diff --git a/webroot/src/locales/en.json b/webroot/src/locales/en.json index a7c6da656..c0f2b68c0 100644 --- a/webroot/src/locales/en.json +++ b/webroot/src/locales/en.json @@ -542,8 +542,10 @@ "licenseRegistrationTitle": "Live status", "licenseRegistrationEnabled": "Is license registration enabled?", "licenseRegistrationEnabledSubtext": "This enables live status for purchasing privileges, and this action is irreversible.", + "licenseRegistrationEnabledSubtextCosm": "This action grants all eligible licensees the ability to work in other compact states that are also live. This action is irreversible.", "privilegePurchaseEnabled": "State is live for compact privilege purchase", "privilegePurchaseEnabledSubtext": "This enables live status for purchasing privileges, and this action is irreversible.", + "privilegePurchaseEnabledSubtextCosm": "This action grants all eligible licensees in your state the ability to work in other compact states that are also live. Licensees from other live compact states will be automatically granted the ability to work in your state. This action is irreversible.", "confirmSaveCompactTitle": "Are you sure you want to mark this state as live?", "confirmSaveCompactYes": "Yes, mark as live", "saveSuccessfulCompact": "Compact changes saved successfully", @@ -586,6 +588,7 @@ "license": "License", "licensingListTitle": "Licensing Data", "licensingListDescription": "Click on a licensee to view the status of their privilege(s). The “Privileges” column includes both past and active privileges.", + "licensingListDescriptionCosm": "Click on a licensee to view details about where they can work under the Compact.", "attestation": "Attestation", "stateProvidedEmail": "State-provided email", "stateEmail": "State email", diff --git a/webroot/src/locales/es.json b/webroot/src/locales/es.json index d4f88924b..2d29345b9 100644 --- a/webroot/src/locales/es.json +++ b/webroot/src/locales/es.json @@ -525,9 +525,11 @@ "summaryReportEmailsSubtext": "Ingrese todos los destinatarios que recibirán informes resumidos periódicos.", "licenseRegistrationTitle": "Estado en vivo", "licenseRegistrationEnabled": "¿Está habilitado el registro de licencia?", - "licenseRegistrationEnabledSubtext": "Esto habilita el estado en vivo para los privilegios de compra, y esta acción es irreversible.", + "licenseRegistrationEnabledSubtext": "Esto habilita el estado en tiempo real para los privilegios de compra, y esta acción es irreversible.", + "licenseRegistrationEnabledSubtextCosm": "Esta medida otorga a todos los licenciatarios elegibles la capacidad de trabajar en otros estados miembros del pacto que también estén vigentes. Esta medida es irreversible.", "privilegePurchaseEnabled": "El estado está en línea para la compra de privilegios compactos", - "privilegePurchaseEnabledSubtext": "Esto habilita el estado en vivo para los privilegios de compra, y esta acción es irreversible.", + "privilegePurchaseEnabledSubtext": "Esto habilita el estado en tiempo real para los privilegios de compra, y esta acción es irreversible.", + "privilegePurchaseEnabledSubtextCosm": "Esta acción otorga a todos los licenciatarios elegibles de su estado la capacidad de trabajar en otros estados miembros del acuerdo que también estén activos. Los licenciatarios de otros estados miembros del acuerdo que estén activos obtendrán automáticamente la capacidad de trabajar en su estado. Esta acción es irreversible.", "confirmSaveCompactTitle": "¿Estás seguro de que deseas marcar este estado como activo?", "confirmSaveCompactYes": "Sí, marcar como en vivo", "saveSuccessfulCompact": "Los cambios compactos se guardaron correctamente", @@ -569,6 +571,7 @@ "licensing": { "licensingListTitle": "Datos de licencia", "licensingListDescription": "Haga clic en un licenciatario para ver el estado de sus privilegios. La columna “Privilegios” incluye tanto los privilegios anteriores como los vigentes.", + "licensingListDescriptionCosm": "Haz clic en un licenciatario para ver detalles sobre dónde puede trabajar en virtud del Acuerdo.", "searchTitle": "Iniciar una búsqueda", "searchTitlePublic": "Verificar un privilegio compacto", "attestation": "Atestación", diff --git a/webroot/src/pages/LicensingDetail/LicensingDetail.ts b/webroot/src/pages/LicensingDetail/LicensingDetail.ts index 42e6815eb..6d3763328 100644 --- a/webroot/src/pages/LicensingDetail/LicensingDetail.ts +++ b/webroot/src/pages/LicensingDetail/LicensingDetail.ts @@ -83,10 +83,15 @@ export default class LicensingDetail extends Vue { } get hasLoggedInReadSsnAccessForLicensee(): boolean { - const { compact, loggedInUser, licenseeStates } = this; + const { + compact, + loggedInUser, + isAppModeCosmetology, + licenseeStates + } = this; let hasLoggedInReadSsnAccess = false; - if (compact && loggedInUser) { + if (compact && loggedInUser && !isAppModeCosmetology) { hasLoggedInReadSsnAccess = licenseeStates.some((state) => loggedInUser.hasPermission( Permission.READ_SSN, this.compact as CompactType, diff --git a/webroot/src/pages/LicensingDetail/LicensingDetail.vue b/webroot/src/pages/LicensingDetail/LicensingDetail.vue index 1c02d9622..788b656b9 100644 --- a/webroot/src/pages/LicensingDetail/LicensingDetail.vue +++ b/webroot/src/pages/LicensingDetail/LicensingDetail.vue @@ -91,7 +91,7 @@
-
+
{{$t('licensing.registrationEmail')}}
diff --git a/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.less b/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.less index 32ec3e747..0cb979bc0 100644 --- a/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.less +++ b/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.less @@ -124,6 +124,69 @@ } } + .license-section { + margin: @spacingMobile; + padding-top: @spacingMobile; + border-top: 1px solid @lightGrey; + + @media @tabletWidth { + margin: @spacingMobile @spacingTablet; + padding-top: @spacingMobile; + } + + @media @desktopWidth { + margin: @spacingMobile @spacingDesktop; + padding-top: @spacingMobile; + } + + .license-card-list-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + @media @tabletWidth { + flex-direction: row; + flex-wrap: wrap; + align-items: flex-start; + justify-content: flex-start; + width: 100%; + } + + .license-chunk { + .license-expired-message { + margin-top: 1rem; + color: @midRed; + } + } + } + + .home-state-section { + display: flex; + flex-direction: column; + + .home-state-list { + display: flex; + flex-direction: column; + + @media @tabletWidth { + flex-direction: row; + } + } + + .homestate-error-text { + max-width: 20rem; + padding: 0 1rem; + color: @midRed; + text-overflow: wrap; + + @media @tabletWidth { + max-width: 40rem; + } + } + } + } + .privilege-section { margin: @spacingMobile; padding-top: @spacingMobile; diff --git a/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.ts b/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.ts index 13c68cb64..ec5c6298d 100644 --- a/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.ts +++ b/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.ts @@ -7,16 +7,20 @@ import { Component, Vue } from 'vue-facing-decorator'; import LoadingSpinner from '@components/LoadingSpinner/LoadingSpinner.vue'; +import LicenseCard from '@/components/LicenseCard/LicenseCard.vue'; import PrivilegeCard from '@/components/PrivilegeCard/PrivilegeCard.vue'; import CollapseCaretButton from '@components/CollapseCaretButton/CollapseCaretButton.vue'; import ExpirationExplanationIcon from '@components/Icons/ExpirationExplanationIcon/ExpirationExplanationIcon.vue'; +import LicenseIcon from '@components/Icons/LicenseIcon/LicenseIcon.vue'; import { Licensee } from '@models/Licensee/Licensee.model'; -import { License } from '@models/License/License.model'; +import { License, LicenseStatus } from '@models/License/License.model'; @Component({ name: 'PublicLicensingDetail', components: { LoadingSpinner, + LicenseIcon, + LicenseCard, PrivilegeCard, CollapseCaretButton, ExpirationExplanationIcon @@ -26,6 +30,7 @@ export default class PublicLicensingDetail extends Vue { // // Data // + isLicensesCollapsed = false; isPrivsCollapsed = false; // @@ -48,6 +53,14 @@ export default class PublicLicensingDetail extends Vue { return this.$store.state.user; } + get isAppModeJcc(): boolean { + return this.$store.getters.isAppModeJcc; + } + + get isAppModeCosmetology(): boolean { + return this.$store.getters.isAppModeCosmetology; + } + get compact(): string { const defaultCompactType = this.userStore.currentCompact?.type; @@ -85,14 +98,27 @@ export default class PublicLicensingDetail extends Vue { return this.licenseStore?.isLoading || false; } + get licenseeLicenses(): Array { + return (this.licensee?.licenses || []).slice().sort(this.sortLicenses); + } + + get activeLicenses(): Array { + return this.licenseeLicenses.filter((license) => (license.status === LicenseStatus.ACTIVE)); + } + get licenseePrivileges(): Array { - return this.licensee?.privileges || []; + return (this.licensee?.privileges || []).slice().sort(this.sortPrivileges); } get licenseeStates(): Array { - return this.licenseePrivileges + const licenseStates = this.activeLicenses + .map((license) => license.issueState?.abbrev || '') + .filter((state) => !!state); + const privilegeStates = this.licenseePrivileges .map((privilege) => privilege.issueState?.abbrev || '') .filter((state) => !!state); + + return licenseStates.concat(privilegeStates); } get homeState(): string { @@ -112,7 +138,59 @@ export default class PublicLicensingDetail extends Vue { }); } + toggleLicensesCollapsed(): void { + this.isLicensesCollapsed = !this.isLicensesCollapsed; + } + togglePrivsCollapsed(): void { this.isPrivsCollapsed = !this.isPrivsCollapsed; } + + 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; + } } diff --git a/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.vue b/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.vue index ed0db34a4..e40dabeec 100644 --- a/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.vue +++ b/webroot/src/pages/PublicLicensingDetail/PublicLicensingDetail.vue @@ -35,6 +35,31 @@
+
+
+
+
+ +
+
{{ this.$t('licensing.licenseDetails') }}
+
+ +
+
+
+ +
+
+