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
5 changes: 5 additions & 0 deletions .changeset/red-bushes-matter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sbc-connect/nuxt-business-base": minor
---

maxlength and number modifier on FormShareClass and FormShareSeries, add useFilingAlerts and attach in ManageShareStructure, nextStep, previousStep and scroll to top in useFilingPageWatcher, digit accuracy in formatCurrency, default checkbox styling in app config
10 changes: 9 additions & 1 deletion packages/layers/base/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
export default defineAppConfig({})
export default defineAppConfig({
ui: {
checkbox: {
slots: {
label: 'text-base group-has-[button[aria-invalid=true]]:text-error'
}
}
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,14 @@ const nameInputSlots = computed(() => ({
trailing: h('span', { class: ['text-base font-bold', hasNameError.value ? 'text-error' : ''] }, t('label.shares'))
}))
provide('UInput-slots-share-class-name-input', nameInputSlots)
provide('UInput-props-max-number-shares-input', { maxlength: '17' })
provide('UInput-props-par-value-input', { maxlength: '17' })
</script>

<template>
<UForm
ref="share-class-form"
:data-testid="`${variant}-share-class-form`"
:schema
:name
:nested
Expand Down Expand Up @@ -98,7 +101,7 @@ provide('UInput-slots-share-class-name-input', nameInputSlots)
<template #label="{ item }">
<ConnectFormInput
v-if="item.value"
v-model="model.maxNumberOfShares"
v-model.number="model.maxNumberOfShares"
:disabled="!model.hasMaximumShares"
:class="{ 'opacity-75': !model.hasMaximumShares }"
input-id="max-number-shares-input"
Expand All @@ -124,7 +127,7 @@ provide('UInput-slots-share-class-name-input', nameInputSlots)
<template #label="{ item }">
<div v-if="item.value" class="flex flex-col gap-2 sm:gap-4 sm:flex-row">
<ConnectFormInput
v-model="model.parValue"
v-model.number="model.parValue"
:disabled="!model.hasParValue"
:class="{ 'opacity-75': !model.hasParValue }"
input-id="par-value-input"
Expand All @@ -143,6 +146,7 @@ provide('UInput-slots-share-class-name-input', nameInputSlots)
<ConnectInputMenu
id="par-value-currency-input"
v-model="model.currency"
data-testid="par-value-currency-input"
:label="$t('label.currency')"
:items="currencyOptions"
class="w-full"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ const nameInputSlots = computed(() => ({
trailing: h('span', { class: ['text-base font-bold', hasNameError.value ? 'text-error' : ''] }, t('label.shares'))
}))
provide('UInput-slots-share-series-name-input', nameInputSlots)
provide('UInput-props-max-number-shares-input', { maxlength: '17' })
</script>

<template>
<UForm
ref="share-series-form"
:data-testid="`${variant}-share-series-form`"
:schema
:name
:nested
Expand Down Expand Up @@ -122,7 +124,7 @@ provide('UInput-slots-share-series-name-input', nameInputSlots)
<template #label="{ item }">
<ConnectFormInput
v-if="item.value"
v-model="model.maxNumberOfShares"
v-model.number="model.maxNumberOfShares"
:disabled="!model.hasMaximumShares"
:class="{ 'opacity-75': !model.hasMaximumShares }"
input-id="max-number-shares-input"
Expand All @@ -136,7 +138,8 @@ provide('UInput-slots-share-series-name-input', nameInputSlots)
</URadioGroup>
<ConnectFormInput
v-else
v-model="model.maxNumberOfShares"
v-model.number="model.maxNumberOfShares"
maxlength="17"
:disabled="!model.hasMaximumShares"
:class="{ 'opacity-75': !model.hasMaximumShares }"
input-id="max-number-shares-input"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ const {

const { t } = useI18n()
const {
alerts,
setAlert,
clearAlert
clearAlert,
attachAlerts
} = useFilingAlerts(stateKey)
const { targetId, messageId } = attachAlerts(stateKey, activeClass)
const { setAlertText } = useConnectButtonControl()
const activeClassSchema = getActiveShareClassSchema()
const activeSeriesSchema = getActiveShareSeriesSchema()
Expand Down Expand Up @@ -139,14 +142,23 @@ function clearAllAlerts() {
<p class="pb-4">
{{ $t('text.shareStructureMustMatchCompanysCurrentState') }}
</p>
<UButton
v-if="(!allowedActions || allowedActions.includes(ManageAllowedAction.ADD)) && !readonly"
:label="addLabel"
variant="outline"
icon="i-mdi-account-plus-outline"
class="w-min"
@click="initAddItem()"
/>
<div class="flex flex-wrap gap-4 items-center">
<UButton
v-if="(!allowedActions || allowedActions.includes(ManageAllowedAction.ADD)) && !readonly"
:data-alert-focus-target="targetId"
:label="addLabel"
:aria-describedby="messageId"
variant="outline"
icon="i-mdi-account-plus-outline"
class="w-min"
@click="initAddItem()"
/>
<FormAlertMessage
:id="messageId"
:message="alerts[stateKey]"
/>
</div>

<FormShareClass
v-if="addingShareClass && activeClass"
v-model="activeClass"
Expand All @@ -155,6 +167,7 @@ function clearAllAlerts() {
variant="add"
name="activeClass"
:validation-context="classValidationContext"
nested
@done="() => { addNewShareClass(activeClass), cleanupForm() }"
@cancel="cleanupForm"
/>
Expand Down Expand Up @@ -185,6 +198,7 @@ function clearAllAlerts() {
variant="edit"
name="activeClass"
:validation-context="classValidationContext"
nested
@done="() => updateShareClass(row, activeClass, cleanupForm)"
@cancel="cleanupForm"
@remove="() => removeShareClass(row, cleanupForm)"
Expand All @@ -197,6 +211,7 @@ function clearAllAlerts() {
:variant="addingSeriesToClassId ? 'add' : 'edit'"
:row
:state-key="stateKey"
nested
@done="() => {
addingSeriesToClassId ? addNewShareSeries(row, activeSeries) : updateShareSeries(row, activeSeries)
cleanupForm()
Expand Down
23 changes: 20 additions & 3 deletions packages/layers/base/app/composables/useFilingPageWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ export function useFilingPageWatcher<T>(options: FilingPageWatcherOptions<T>) {
}
})

function nextStep() {
_currentStep.value++
document.querySelector('main')?.scrollIntoView({
behavior: 'smooth',
block: 'start'
})
}
function previousStep() {
_currentStep.value--
document.querySelector('main')?.scrollIntoView({
behavior: 'smooth',
block: 'start'
})
}

function updateButtonControl() {
const isMultiStep = !!options.steps && options.steps.length > 0
const stepIndex = currentStep.value - 1
Expand All @@ -62,7 +77,7 @@ export function useFilingPageWatcher<T>(options: FilingPageWatcherOptions<T>) {
label: t('label.back'),
variant: 'outline' as const,
icon: 'i-mdi-chevron-left',
onClick: () => { currentStep.value-- },
onClick: previousStep,
...options.backButton,
...(step?.backButton || {})
},
Expand All @@ -82,7 +97,7 @@ export function useFilingPageWatcher<T>(options: FilingPageWatcherOptions<T>) {
label: isLastStep ? t('label.submit') : t('label.next'),
trailingIcon: 'i-mdi-chevron-right',
type: isLastStep ? 'submit' : 'button',
onClick: !isLastStep ? () => { currentStep.value++ } : undefined,
onClick: !isLastStep ? () => { nextStep() } : undefined,
...options.submitFiling,
...(step?.submitFiling || {})
}
Expand Down Expand Up @@ -147,6 +162,8 @@ export function useFilingPageWatcher<T>(options: FilingPageWatcherOptions<T>) {
)

return {
currentStep
currentStep,
nextStep,
previousStep
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function getParValueColumn<T extends ShareClassSchema>(

const parValue = row.original.new.parValue
const displayText = parValue
? formatCurrency(parValue, row.original.new.currency ?? '')
? formatCurrency(parValue, row.original.new.currency ?? '', 6)
: t('label.noParValue')

return h(
Expand Down
5 changes: 3 additions & 2 deletions packages/layers/base/app/utils/currency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ export function getCurrencyList() {
}

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
export function formatCurrency(amount: number, currency: string) {
export function formatCurrency(amount: number, currency: string, maximumDigits: number = 2) {
const locale = useNuxtApp().$i18n.locale.value
try {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency,
currencyDisplay: 'narrowSymbol'
currencyDisplay: 'narrowSymbol',
maximumFractionDigits: maximumDigits
}).format(amount)
} catch {
// will throw an error if an invalid currency was provided
Expand Down
7 changes: 4 additions & 3 deletions packages/layers/base/app/utils/format-share-classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ export function formatShareClassesUi(
classes: ShareClass[]
// config = 'table' // add other format config as necessary
): TableBusinessState<ShareClassSchema>[] {
const baseFormatter = <T extends { id: number | string, name: string }>(item: T) => ({
const baseFormatter = <T extends { id: number | string, name: string, actions?: ActionType[] }>(item: T) => ({
...item,
id: item.id.toString(),
actions: [],
actions: item.actions ?? [],
name: item.name.replace(/\s*\b(shares|share|value)\b/gi, '').trim()
})

return classes.map((c) => {
const formattedSeries = c.series.map(s => ({
...baseFormatter(s),
isInvalid: false
// eslint-disable-next-line @typescript-eslint/no-explicit-any
isInvalid: (s as any).isInvalid ?? false
}))

const formattedClass = {
Expand Down
67 changes: 64 additions & 3 deletions web/corps/app/components/Form/Transition/Step1.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
<script setup lang="ts">
import type { Form } from '@nuxt/ui'
import { z } from 'zod'

const { t } = useI18n()
const store = useTransitionStore()
const activeOffice = ref<ActiveOfficesSchema | undefined>(undefined)

const schema = z.object({
confirmOffices: z.boolean().refine(val => val === true, t('connect.validation.required')),
confirmDirectors: z.boolean().refine(val => val === true, t('connect.validation.required'))
})

type TAStep1Schema = z.output<typeof schema>

const formRef = useTemplateRef<Form<TAStep1Schema>>('ta-form-step-1')

const confirmErrors = computed(() => {
const errors = formRef.value?.getErrors()

return {
confirmOffices: errors?.find(e => e.name?.includes('confirmOffices')),
confirmDirectors: errors?.find(e => e.name?.includes('confirmDirectors'))
}
})
</script>

<template>
<div class="space-y-6 sm:space-y-10">
<UForm
ref="ta-form-step-1"
:state="store.formState"
:schema
novalidate
class="space-y-6 sm:space-y-10"
@error="onFormSubmitError"
>
<section class="space-y-4" data-testid="office-addresses-section">
<div>
<h2 class="text-base">
Expand All @@ -21,6 +50,20 @@ const activeOffice = ref<ActiveOfficesSchema | undefined>(undefined)
:edit-label="$t('label.editOffice')"
:allowed-actions="[]"
/>

<ConnectFormFieldWrapper
orientation="horizontal"
:label="$t('label.confirm')"
class="bg-white p-6 rounded"
:error="confirmErrors.confirmOffices"
>
<UFormField name="confirmOffices" :ui="{ error: 'sr-only' }">
<UCheckbox
v-model="store.formState.confirmOffices"
:label="$t('text.confirmOfficesCorrect')"
/>
</UFormField>
</ConnectFormFieldWrapper>
</section>

<section class="space-y-4" data-testid="current-directors-section">
Expand All @@ -40,7 +83,22 @@ const activeOffice = ref<ActiveOfficesSchema | undefined>(undefined)
:role-type="RoleTypeUi.DIRECTOR"
:allowed-actions="[ManageAllowedAction.ADDRESS_CHANGE]"
:columns-to-display="['name', 'delivery', 'mailing', 'effectiveDates', 'actions']"
form-party-details-name="activeDirector"
/>

<ConnectFormFieldWrapper
orientation="horizontal"
:label="$t('label.confirm')"
class="bg-white p-6 rounded"
:error="confirmErrors.confirmDirectors"
>
<UFormField name="confirmDirectors" :ui="{ error: 'sr-only' }">
<UCheckbox
v-model="store.formState.confirmDirectors"
:label="$t('text.confirmDirectorsCorrect')"
/>
</UFormField>
</ConnectFormFieldWrapper>
</section>

<section data-testid="share-structure-section">
Expand All @@ -52,9 +110,12 @@ const activeOffice = ref<ActiveOfficesSchema | undefined>(undefined)
v-model:active-class="store.formState.activeClass"
v-model:active-series="store.formState.activeSeries"
:loading="store.initializing"
:empty-text="store.initializing ? `${$t('label.loading')}...` : $t('label.noShareClasses')"
:empty-text="store.initializing
? `${$t('label.loading')}...`
: $t('label.noShareClasses')
"
:add-label="$t('label.addShareClass')"
/>
</section>
</div>
</UForm>
</template>
Loading
Loading