Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5761f5e
WIP: refactor backend to be multi-service
jusdino Sep 17, 2025
30c31d2
Merge branch 'development' into feat/social-work-reorg
jusdino Sep 17, 2025
88dee92
Docs, common deploy resource stack
jusdino Sep 19, 2025
0b4a1a8
Merge branch 'development' into feat/social-work-reorg
jusdino Sep 19, 2025
e2df6c2
Move base pipeline stack to common
jusdino Sep 19, 2025
05e3f1a
Update compact-connect deps
jusdino Sep 22, 2025
29eccc0
Move new frontend tests to frontend app, linting
jusdino Sep 24, 2025
20435f5
Update pipelines to v2
jusdino Sep 24, 2025
600ac63
Remove unsupported commit hook example
jusdino Sep 27, 2025
1794203
Use pipeline role in synth steps
jusdino Sep 27, 2025
6e3b6e6
Merge branch 'development' into feat/social-work-reorg
jusdino Sep 29, 2025
31d208c
Use cross-account role for CodeBuild steps
jusdino Sep 30, 2025
b8ad3df
Remove duplicate app_client files
jusdino Sep 30, 2025
322c09b
Remove duplicate app_client files
jusdino Sep 30, 2025
f4d5f0f
WIP: refactor backend to be multi-service
jusdino Sep 17, 2025
fa7ff53
Docs, common deploy resource stack
jusdino Sep 19, 2025
c759a40
Move base pipeline stack to common
jusdino Sep 19, 2025
e88f237
Update compact-connect deps
jusdino Sep 22, 2025
7887af2
Move new frontend tests to frontend app, linting
jusdino Sep 24, 2025
b2882b5
Update pipelines to v2
jusdino Sep 24, 2025
f40792e
Remove unsupported commit hook example
jusdino Sep 27, 2025
7df4820
Use pipeline role in synth steps
jusdino Sep 27, 2025
280307f
Use cross-account role for CodeBuild steps
jusdino Sep 30, 2025
9ead102
Remove duplicate app_client files
jusdino Sep 30, 2025
9322f9f
Merge branch 'tmp' into development
jusdino Sep 30, 2025
3a54823
Merge branch 'development' into feat/social-work-reorg
jusdino Sep 30, 2025
64c80a1
Clean up split nodejs lambda configs
jusdino Oct 1, 2025
038f769
Clean up `cc-ui-app`
jusdino Oct 1, 2025
8560fe7
Split front/back ssm context
jusdino Oct 1, 2025
69a2687
Add ui ssm put script
jusdino Oct 1, 2025
a3c2d7a
WIP: adverse action category multi-select
jsandoval81 Oct 6, 2025
9b10b79
Merge branch 'development' into frontend/aa-multi-select
jsandoval81 Oct 7, 2025
0b56ec5
WIP: adverse action category multi-select
jsandoval81 Oct 7, 2025
ef2391c
WIP: adverse action category multi-select
jsandoval81 Oct 9, 2025
c168c8f
Merge branch 'development' into frontend/aa-multi-select
jsandoval81 Oct 15, 2025
03f56e9
WIP: adverse action category multi-select
jsandoval81 Oct 15, 2025
c845560
WIP: adverse action category multi-select
jsandoval81 Oct 15, 2025
97ed2d0
WIP: adverse action category multi-select
jsandoval81 Oct 17, 2025
7c01b09
WIP: adverse action category multi-select
jsandoval81 Oct 21, 2025
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
1 change: 1 addition & 0 deletions webroot/src/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ export const compacts = {
// = Feature gate IDs =
// =============================
export enum FeatureGates {
ENCUMBER_MULTI_CATEGORY = 'encumbrance-multi-category-flag',
EXAMPLE_FEATURE_1 = 'test-feature-1', // Keep this ID in place for examples & tests
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// InputSelectMultiple.less
// CompactConnect
//
// Created by InspiringApps on 10/2/2025.
//

.input-container {
.multi-select-description {
display: none;
font-size: @fontSizeSmaller;
font-style: italic;

@media (hover: hover) {
display: flex;
}
}

.select-dropdown {
resize: vertical;
}

.selected-container {
display: flex;
flex-wrap: wrap;
width: 100%;
margin-top: 2rem;

.selected-value {
@removeSize: 3.2rem;

display: flex;
flex-grow: 0;
flex-shrink: 1;
align-items: center;
width: fit-content;
min-height: @removeSize;
margin-bottom: 1rem;
padding: 0 @removeSize 0 0.8rem;
border: 1px solid @fontColor;
border-radius: @borderRadiusPillShape;
color: @white;
font-size: @fontSize;
background-color: @fontColor;

&:not(:last-child) {
margin-right: 1rem;
}

.remove {
position: absolute;
top: 50%;
right: 0;
width: @removeSize;
height: @removeSize;
margin-left: auto;
transform: translateY(-50%);
cursor: pointer;
stroke: @white;
}
}
Comment thread
jsandoval81 marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// InputSelectMultiple.spec.ts
// CompactConnect
//
// Created by InspiringApps on 10/2/2025.
//

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

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

expect(wrapper.exists()).to.equal(true);
expect(wrapper.findComponent(InputSelectMultiple).exists()).to.equal(true);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// InputSelectMultiple.ts
// CompactConnect
//
// Created by InspiringApps on 10/2/2025.
//

import {
Component,
mixins,
toNative
} from 'vue-facing-decorator';
import { ComputedRef } from 'vue';
import MixinInput from '@components/Forms/_mixins/input.mixin';
import CloseXIcon from '@components/Icons/CloseX/CloseX.vue';

interface SelectOption {
value: string | number;
name: string | ComputedRef<string>;
}

@Component({
name: 'InputSelectMultiple',
components: {
CloseXIcon,
},
})
class InputSelectMultiple extends mixins(MixinInput) {
//
// Methods
//
getValueDisplay(value = ''): string | ComputedRef<string> {
const selectedOption: SelectOption = this.formInput?.valueOptions?.find((option: SelectOption) =>
option.value === value) || { value, name: '' };

return selectedOption?.name || '';
}

removeSelectedValue(value): void {
const { formInput } = this;

if (Array.isArray(formInput.value)) {
(formInput.value as Array<string>) = formInput.value.filter((selected) => selected !== value);
}

formInput.validate();
}
}

export default toNative(InputSelectMultiple);

// export default InputSelectMultiple;
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!--
InputSelectMultiple.vue
CompactConnect

Created by InspiringApps on 10/2/2025.
-->

<template>
<div
class="input-container"
:class="{
'form-row': formInput.isFormRow,
'no-margin': formInput.shouldHideMargin,
'has-error': !!formInput.errorMessage
}"
>
<label
v-if="!formInput.shouldHideLabel"
:for="formInput.id"
>
{{ formInput.label }}
<span v-if="isRequired" class="required-indicator">*</span>
</label>
<div class="multi-select-description">{{ $t('common.selectMultipleKeys') }}</div>
<select
multiple="multiple"
:id="formInput.id"
:name="formInput.name"
v-model="formInput.value"
:aria-label="formInput.label"
:aria-describedby="`${formInput.id}-error`"
:aria-errormessage="`${formInput.id}-error`"
:aria-invalid="!!formInput.errorMessage"
:autocomplete="formInput.autocomplete"
@blur="blur(formInput)"
@change="input(formInput)"
class="select-dropdown"
:class="{
'has-error': !!formInput.errorMessage
}"
:disabled="formInput.isDisabled"
>
<option
v-for="(option, index) in formInput.valueOptions"
:key="index"
:value="option.value"
:disabled="option.isDisabled"
>
{{ option.name }}
</option>
</select>
<span
v-if="formInput.errorMessage && !formInput.shouldHideErrorMessage"
:id="`${formInput.id}-error`"
class="form-field-error"
role="alert"
aria-live="assertive"
>
{{ formInput.errorMessage }}
</span>
<div class="selected-container">
<div
v-for="(value, index) in formInput.value"
:key="index"
class="selected-value"
>
{{ getValueDisplay(value) }}
<div
class="remove"
:aria-label="`${$t('common.remove')} ${getValueDisplay(value)}`"
role="button"
@click="removeSelectedValue(value)"
@keyup.enter="removeSelectedValue(value)"
tabindex="0"
>
<CloseXIcon />
</div>
</div>
</div>
Comment thread
jsandoval81 marked this conversation as resolved.
</div>
</template>

<script lang="ts" src="./InputSelectMultiple.ts"></script>
<style scoped lang="less" src="./InputSelectMultiple.less"></style>
63 changes: 48 additions & 15 deletions webroot/src/components/LicenseCard/LicenseCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import {
ComputedRef,
nextTick
} from 'vue';
import { dateFormatPatterns } from '@/app.config';
import { dateFormatPatterns, FeatureGates } 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';
import InputSelectMultiple from '@components/Forms/InputSelectMultiple/InputSelectMultiple.vue';
import InputCheckbox from '@components/Forms/InputCheckbox/InputCheckbox.vue';
import InputButton from '@components/Forms/InputButton/InputButton.vue';
import InputSubmit from '@components/Forms/InputSubmit/InputSubmit.vue';
Expand All @@ -46,6 +47,7 @@ import moment from 'moment';
MockPopulate,
InputDate,
InputSelect,
InputSelectMultiple,
InputCheckbox,
InputButton,
InputSubmit,
Expand Down Expand Up @@ -89,6 +91,10 @@ class LicenseCard extends mixins(MixinForm) {
//
// Computed
//
get featureGates(): typeof FeatureGates {
return FeatureGates;
}

get userStore() {
return this.$store.state.user;
}
Expand Down Expand Up @@ -236,10 +242,12 @@ class LicenseCard extends mixins(MixinForm) {
name: npdbType.name,
}));

options.unshift({
value: '',
name: computed(() => this.$t('common.selectOption')),
});
if (!this.$features.checkGate(FeatureGates.ENCUMBER_MULTI_CATEGORY)) {
options.unshift({
value: '',
name: computed(() => this.$t('common.selectOption')),
});
}

return options;
}
Expand Down Expand Up @@ -272,13 +280,27 @@ class LicenseCard extends mixins(MixinForm) {
validation: Joi.string().required().messages(this.joiMessages.string),
valueOptions: this.encumberDisciplineOptions,
}),
encumberModalNpdbCategory: new FormInput({
id: 'npdb-category',
name: 'npdb-category',
label: computed(() => this.$t('licensing.npdbCategoryLabel')),
validation: Joi.string().required().messages(this.joiMessages.string),
valueOptions: this.npdbCategoryOptions,
}),
...(this.$features.checkGate(FeatureGates.ENCUMBER_MULTI_CATEGORY)
? {
encumberModalNpdbCategories: new FormInput({
id: 'npdb-categories',
name: 'npdb-categories',
label: computed(() => this.$t('licensing.npdbCategoryLabel')),
validation: Joi.array().min(1).messages(this.joiMessages.array),
valueOptions: this.npdbCategoryOptions,
value: [],
}),
}
: {
encumberModalNpdbCategory: new FormInput({
id: 'npdb-category',
name: 'npdb-category',
label: computed(() => this.$t('licensing.npdbCategoryLabel')),
validation: Joi.string().required().messages(this.joiMessages.string),
valueOptions: this.npdbCategoryOptions,
}),
}
),
encumberModalStartDate: new FormInput({
id: 'encumber-start',
name: 'encumber-start',
Expand Down Expand Up @@ -310,7 +332,7 @@ class LicenseCard extends mixins(MixinForm) {
const adverseActionInput = new FormInput({
id: `adverse-action-data-${adverseActionId}`,
name: `adverse-action-data-${adverseActionId}`,
label: adverseAction.npdbTypeName(),
label: adverseAction.encumbranceTypeName(),
isDisabled: Boolean(adverseAction.endDate),
});

Expand Down Expand Up @@ -419,7 +441,14 @@ class LicenseCard extends mixins(MixinForm) {
licenseState: stateAbbrev,
licenseType: licenseTypeAbbrev.toLowerCase(),
encumbranceType: this.formData.encumberModalDisciplineAction.value,
npdbCategory: this.formData.encumberModalNpdbCategory.value,
...(this.$features.checkGate(FeatureGates.ENCUMBER_MULTI_CATEGORY)
? {
npdbCategories: this.formData.encumberModalNpdbCategories.value,
}
: {
npdbCategory: this.formData.encumberModalNpdbCategory.value,
}
),
startDate: this.formData.encumberModalStartDate.value,
}).catch((err) => {
this.modalErrorMessage = err?.message || this.$t('common.error');
Expand Down Expand Up @@ -619,7 +648,11 @@ class LicenseCard extends mixins(MixinForm) {
this.validateAll({ asTouched: true });
} else if (this.isEncumberLicenseModalDisplayed) {
this.formData.encumberModalDisciplineAction.value = this.encumberDisciplineOptions[1]?.value;
this.formData.encumberModalNpdbCategory.value = this.npdbCategoryOptions[1]?.value;
if (this.$features.checkGate(FeatureGates.ENCUMBER_MULTI_CATEGORY)) {
this.formData.encumberModalNpdbCategories.value = [this.npdbCategoryOptions[1]?.value];
} else {
this.formData.encumberModalNpdbCategory.value = this.npdbCategoryOptions[1]?.value;
}
this.formData.encumberModalStartDate.value = moment().format('YYYY-MM-DD');
await nextTick();
this.validateAll({ asTouched: true });
Expand Down
5 changes: 4 additions & 1 deletion webroot/src/components/LicenseCard/LicenseCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@
<div class="form-row">
<InputSelect :formInput="formData.encumberModalDisciplineAction" />
</div>
<div class="form-row">
<div v-if="$features.checkGate(featureGates.ENCUMBER_MULTI_CATEGORY)" class="form-row">
<InputSelectMultiple :formInput="formData.encumberModalNpdbCategories" />
</div>
<div v-else class="form-row">
<InputSelect :formInput="formData.encumberModalNpdbCategory" />
</div>
<div class="form-row">
Expand Down
Loading