diff --git a/strr-base-web/app/composables/useStrrModals.ts b/strr-base-web/app/composables/useStrrModals.ts index 4e359b9bc..83d3d5d97 100644 --- a/strr-base-web/app/composables/useStrrModals.ts +++ b/strr-base-web/app/composables/useStrrModals.ts @@ -99,16 +99,31 @@ export const useStrrModals = () => { function openHelpRegisterModal () { modal.open(ModalHelpRegisterStr, { + // @ts-expect-error - actions prop is passed down from ModalHelpRegisterStr -> ModalBase actions: [{ label: t('btn.close'), handler: () => close() }] }) } function openInfoCollectionNoticeModal () { modal.open(ModalInfoCollectionNotice, { + // @ts-expect-error - actions prop is passed down from ModalInfoCollectionNotice -> ModalBase actions: [{ label: t('btn.close'), handler: () => close() }] }) } + function openErrorModal (title: string, description: string, showContactInfo: boolean) { + modal.open(ModalBase, { + error: { + title, + description, + showContactInfo + }, + actions: [ + { label: t('btn.close'), handler: () => close() } + ] + }) + } + function close () { modal.close() } @@ -121,6 +136,7 @@ export const useStrrModals = () => { openConfirmSwitchAccountModal, openHelpRegisterModal, openInfoCollectionNoticeModal, + openErrorModal, close } } diff --git a/strr-host-pm-web/app/locales/en-CA.ts b/strr-host-pm-web/app/locales/en-CA.ts index 491ad529d..caf0ea279 100644 --- a/strr-host-pm-web/app/locales/en-CA.ts +++ b/strr-host-pm-web/app/locales/en-CA.ts @@ -86,6 +86,12 @@ export default { saveStartApplication: 'Save & Start Application', createNewReg: 'Create New Registration' }, + error: { + createAccount: { + title: 'Error creating account', + description: 'We could not create your account at this time. Please try again or if this issue persists, please contact us.' + } + }, label: { hotelName: 'Hotel Name', expiryDate: 'Expiry Date', diff --git a/strr-host-pm-web/app/pages/auth/account/create-new.vue b/strr-host-pm-web/app/pages/auth/account/create-new.vue index 5a3861643..3a029b3be 100644 --- a/strr-host-pm-web/app/pages/auth/account/create-new.vue +++ b/strr-host-pm-web/app/pages/auth/account/create-new.vue @@ -10,20 +10,21 @@ const isSmallScreen = useMediaQuery('(max-width: 640px)') const accountStore = useConnectAccountStore() const { compPartySchema } = useStrrContactStore() const { completingParty } = storeToRefs(useStrrContactStore()) -const loading = ref(false) +const loadingSubmitForm = ref(false) +const loadingCheckAccountName = ref(false) +const accountExists = ref(false) -const createAccountSchema = compPartySchema.extend({ - accountName: z - .string() - .trim() - .min(1, t('validation.accountName.required')) // check for a non empty string - .refine( // check account doesnt already exist - name => !accountStore.userAccounts.some(account => account.label === name), - t('validation.accountName.exists') - ) -}) +const createAccountSchema = computed( + () => compPartySchema.extend({ + accountName: z + .string() + .trim() + .min(1, t('validation.accountName.required')) // check for a non empty string + .refine(() => accountExists.value !== true, t('validation.accountName.exists')) + }) +) -type CreateAccountSchema = z.output +type CreateAccountSchema = z.output const state = reactive({ ...completingParty.value, @@ -32,20 +33,10 @@ const state = reactive({ const createAccountFormRef = ref>() -useHead({ - title: t('page.createAccount.title') -}) - -definePageMeta({ - middleware: ['auth', 'check-tos'], - hideBreadcrumbs: true -}) - async function handleCreateAccount () { try { - loading.value = true - - const response = await $strrApi<{sbc_account_id: number, user_id: number}>('/accounts', { + loadingSubmitForm.value = true + const response = await $strrApi<{sbc_account_id: number, user_id: number}>('/accounts', { // TODO: move function to store/composable? method: 'POST', body: { name: state.accountName, @@ -61,12 +52,48 @@ async function handleCreateAccount () { await navigateTo(localePath('/application')) } } catch (e) { - strrModal.openAppSubmitError(e) // TODO: better error message + strrModal.openErrorModal(t('error.createAccount.title'), t('error.createAccount.description'), true) logFetchError(e, 'Unable to create account') } finally { - loading.value = false + loadingSubmitForm.value = false } } + +// validate account name already exists +watchDebounced( + () => state.accountName, + async (newVal) => { + if (newVal.trim() !== '') { + try { + loadingCheckAccountName.value = true + // also returns limit: number, orgs: Array, page: number, but we only need the total here + const { total } = await $strrApi<{ total: number }>('/accounts/search', { // TODO: move function to store/composable? + params: { name: newVal } + }) + accountExists.value = total > 0 + } catch (e) { + logFetchError(e, 'Error checking if account name exists') + accountExists.value = true // assume account exists if api error + } finally { + loadingCheckAccountName.value = false + } + } else { + accountExists.value = false // set to false if accountName is empty + } + // revalidate input after accountExists ref is updated + createAccountFormRef.value?.validate('accountName', { silent: true }) + }, + { debounce: 150 } // short debounce to get the 'on input' real time validation, this may need to be tweaked +) + +useHead({ + title: t('page.createAccount.title') +}) + +definePageMeta({ + middleware: ['auth', 'check-tos'], + hideBreadcrumbs: true +})