@sbc-connect/nuxt-auth 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/app/components/Connect/Account/Create/index.vue +140 -0
- package/app/pages/auth/account/select.vue +43 -5
- package/app/stores/connect-account.ts +12 -0
- package/app/utils/schemas/account.ts +54 -0
- package/i18n/locales/en-CA.ts +21 -1
- package/nuxt.config.ts +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @sbc-connect/nuxt-auth
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#108](https://github.com/bcgov/connect-nuxt/pull/108) [`8571754`](https://github.com/bcgov/connect-nuxt/commit/8571754160242c3233838fc039fd1a01ff9d7a82) Thanks [@cameron-eyds](https://github.com/cameron-eyds)! - Implements account creation in Auth and Country Code/Country Flag components in Forms
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`8571754`](https://github.com/bcgov/connect-nuxt/commit/8571754160242c3233838fc039fd1a01ff9d7a82), [`d9d6356`](https://github.com/bcgov/connect-nuxt/commit/d9d63560e27117c43b9e8e54d363c3fdbb620049), [`dd17349`](https://github.com/bcgov/connect-nuxt/commit/dd173497255f35415b468e17f7192722f604d3c8)]:
|
|
12
|
+
- @sbc-connect/nuxt-forms@0.3.0
|
|
13
|
+
- @sbc-connect/nuxt-base@0.5.0
|
|
14
|
+
|
|
3
15
|
## 0.2.1
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { Form, FormError } from '@nuxt/ui'
|
|
3
|
+
import type { AccountProfileSchema } from '#auth/app/utils/schemas/account'
|
|
4
|
+
import { getAccountCreateSchema } from '#auth/app/utils/schemas/account'
|
|
5
|
+
|
|
6
|
+
const { accountFormState, userFullName } = useConnectAccountStore()
|
|
7
|
+
const accountProfileSchema = getAccountCreateSchema()
|
|
8
|
+
|
|
9
|
+
const formRef = useTemplateRef<Form<AccountProfileSchema>>('account-create-form')
|
|
10
|
+
|
|
11
|
+
const formErrors = computed<{
|
|
12
|
+
accountName: FormError<string> | undefined
|
|
13
|
+
emailAddress: FormError<string> | undefined
|
|
14
|
+
phone: FormError<string> | undefined
|
|
15
|
+
address: FormError<string> | undefined
|
|
16
|
+
}>(() => {
|
|
17
|
+
const errors = formRef.value?.getErrors()
|
|
18
|
+
return {
|
|
19
|
+
accountName: errors?.find(e => e.name === 'accountName'),
|
|
20
|
+
emailAddress: errors?.find(e => e.name?.startsWith('emailAddress')),
|
|
21
|
+
phone: errors?.find(e => e.name?.startsWith('phone')),
|
|
22
|
+
address: errors?.find(e => e.name?.startsWith('address'))
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
async function validate() {
|
|
27
|
+
return formRef.value?.validate({ silent: true })
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<ConnectPageSection
|
|
33
|
+
:heading="{
|
|
34
|
+
label: $t('connect.label.accountInformation'),
|
|
35
|
+
labelClass: 'font-bold md:ml-4',
|
|
36
|
+
}"
|
|
37
|
+
>
|
|
38
|
+
<UForm
|
|
39
|
+
id="account-create-form"
|
|
40
|
+
ref="account-create-form"
|
|
41
|
+
class="p-8"
|
|
42
|
+
no-validate
|
|
43
|
+
:validate-on="['input', 'change']"
|
|
44
|
+
:schema="accountProfileSchema"
|
|
45
|
+
:state="accountFormState"
|
|
46
|
+
@error="onFormSubmitError"
|
|
47
|
+
>
|
|
48
|
+
<!-- Legal Name -->
|
|
49
|
+
<ConnectFormFieldWrapper
|
|
50
|
+
:label="$t('connect.page.createAccount.yourNameLabel')"
|
|
51
|
+
orientation="horizontal"
|
|
52
|
+
>
|
|
53
|
+
<p class="font-bold">
|
|
54
|
+
{{ userFullName }}
|
|
55
|
+
</p>
|
|
56
|
+
<p class="mt-3">
|
|
57
|
+
{{ $t('connect.page.createAccount.yourNameHelp') }}
|
|
58
|
+
</p>
|
|
59
|
+
</ConnectFormFieldWrapper>
|
|
60
|
+
|
|
61
|
+
<USeparator orientation="horizontal" class="my-8" />
|
|
62
|
+
|
|
63
|
+
<!-- Account Name -->
|
|
64
|
+
<ConnectFormFieldWrapper
|
|
65
|
+
class="pt-2 my-6"
|
|
66
|
+
:label="$t('connect.page.createAccount.accountNameLabel')"
|
|
67
|
+
orientation="horizontal"
|
|
68
|
+
:error="formErrors.accountName"
|
|
69
|
+
>
|
|
70
|
+
<ConnectFormInput
|
|
71
|
+
v-model="accountFormState.accountName"
|
|
72
|
+
name="accountName"
|
|
73
|
+
input-id="account-name-input"
|
|
74
|
+
:label="$t('connect.page.createAccount.accountNameLabel')"
|
|
75
|
+
:help="$t('connect.page.createAccount.accountNameHelp')"
|
|
76
|
+
/>
|
|
77
|
+
</ConnectFormFieldWrapper>
|
|
78
|
+
|
|
79
|
+
<!-- Account Email -->
|
|
80
|
+
<ConnectFormFieldWrapper
|
|
81
|
+
class="my-6"
|
|
82
|
+
:label="$t('connect.page.createAccount.emailLabel')"
|
|
83
|
+
orientation="horizontal"
|
|
84
|
+
:error="formErrors.emailAddress"
|
|
85
|
+
>
|
|
86
|
+
<ConnectFormInput
|
|
87
|
+
v-model="accountFormState.emailAddress"
|
|
88
|
+
name="emailAddress"
|
|
89
|
+
input-id="email-input"
|
|
90
|
+
:label="$t('connect.page.createAccount.emailPlaceholder')"
|
|
91
|
+
/>
|
|
92
|
+
</ConnectFormFieldWrapper>
|
|
93
|
+
|
|
94
|
+
<!-- Account Phone -->
|
|
95
|
+
<ConnectFieldset
|
|
96
|
+
:label="$t('connect.page.createAccount.phoneLabel')"
|
|
97
|
+
orientation="horizontal"
|
|
98
|
+
:error="formErrors.phone"
|
|
99
|
+
>
|
|
100
|
+
<div class="flex flex-row gap-2">
|
|
101
|
+
<ConnectFormPhoneCountryCode
|
|
102
|
+
v-model:country-calling-code="accountFormState.phone.countryCode"
|
|
103
|
+
v-model:country-iso2="accountFormState.phone.countryIso2"
|
|
104
|
+
:is-invalid="!accountFormState.phone.countryIso2"
|
|
105
|
+
class="w-40 mt-[-20px]"
|
|
106
|
+
/>
|
|
107
|
+
<ConnectFormInput
|
|
108
|
+
v-model="accountFormState.phone.phoneNumber"
|
|
109
|
+
name="phone.phoneNumber"
|
|
110
|
+
input-id="phone-number-input"
|
|
111
|
+
:label="$t('connect.page.createAccount.phonePlaceholder')"
|
|
112
|
+
mask="(###) ###-####"
|
|
113
|
+
/>
|
|
114
|
+
<ConnectFormInput
|
|
115
|
+
v-model="accountFormState.phone.ext"
|
|
116
|
+
name="phone.ext"
|
|
117
|
+
input-id="phone-ext-input"
|
|
118
|
+
:label="$t('connect.page.createAccount.phoneExtensionLabel')"
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
</ConnectFieldset>
|
|
122
|
+
|
|
123
|
+
<!-- Account Address -->
|
|
124
|
+
<ConnectFieldset
|
|
125
|
+
class="my-6"
|
|
126
|
+
:label="$t('connect.page.createAccount.addressLabel')"
|
|
127
|
+
orientation="horizontal"
|
|
128
|
+
:error="formErrors.address"
|
|
129
|
+
>
|
|
130
|
+
<ConnectFormAddress
|
|
131
|
+
id="account-address"
|
|
132
|
+
v-model="accountFormState.address"
|
|
133
|
+
name="address"
|
|
134
|
+
schema-prefix="address"
|
|
135
|
+
@should-validate="validate"
|
|
136
|
+
/>
|
|
137
|
+
</ConnectFieldset>
|
|
138
|
+
</UForm>
|
|
139
|
+
</ConnectPageSection>
|
|
140
|
+
</template>
|
|
@@ -9,8 +9,16 @@ const rtc = useRuntimeConfig().public
|
|
|
9
9
|
const accountStore = useConnectAccountStore()
|
|
10
10
|
const { authUser } = useConnectAuth()
|
|
11
11
|
const { finalRedirect } = useConnectAccountFlowRedirect()
|
|
12
|
+
const { clearAccountState } = useConnectAccountStore()
|
|
12
13
|
|
|
13
14
|
const addNew = ref(false)
|
|
15
|
+
const pageTitle = computed(() =>
|
|
16
|
+
!addNew.value ? $t('connect.label.existingAccountFound') : $t('connect.label.sbcAccountCreation')
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
useHead({
|
|
20
|
+
title: pageTitle
|
|
21
|
+
})
|
|
14
22
|
|
|
15
23
|
function selectAndRedirect(id: number) {
|
|
16
24
|
accountStore.switchCurrentAccount(id)
|
|
@@ -22,6 +30,11 @@ onBeforeMount(() => {
|
|
|
22
30
|
addNew.value = true
|
|
23
31
|
}
|
|
24
32
|
})
|
|
33
|
+
|
|
34
|
+
const toggleCreateNewAccount = () => {
|
|
35
|
+
addNew.value = !addNew.value
|
|
36
|
+
clearAccountState()
|
|
37
|
+
}
|
|
25
38
|
</script>
|
|
26
39
|
|
|
27
40
|
<template>
|
|
@@ -29,6 +42,9 @@ onBeforeMount(() => {
|
|
|
29
42
|
<ConnectTransitionFade>
|
|
30
43
|
<div class="space-y-6 sm:space-y-10">
|
|
31
44
|
<h1>{{ !addNew ? $t('connect.label.existingAccountFound') : $t('connect.label.sbcAccountCreation') }}</h1>
|
|
45
|
+
<p v-if="addNew">
|
|
46
|
+
{{ $t('connect.label.createNewAccountCont') }}
|
|
47
|
+
</p>
|
|
32
48
|
<ConnectAccountExistingAlert v-if="!addNew" />
|
|
33
49
|
</div>
|
|
34
50
|
</ConnectTransitionFade>
|
|
@@ -40,12 +56,14 @@ onBeforeMount(() => {
|
|
|
40
56
|
@select="selectAndRedirect"
|
|
41
57
|
/>
|
|
42
58
|
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
59
|
+
<ConnectAccountCreate
|
|
60
|
+
v-else
|
|
61
|
+
ref="account-create-form-ref"
|
|
62
|
+
/>
|
|
46
63
|
</ConnectTransitionFade>
|
|
47
64
|
|
|
48
|
-
|
|
65
|
+
<!-- Select Account Actions -->
|
|
66
|
+
<div v-if="!addNew" class="flex justify-center">
|
|
49
67
|
<UButton
|
|
50
68
|
v-if="authUser.loginSource === ConnectLoginSource.BCSC"
|
|
51
69
|
variant="outline"
|
|
@@ -54,7 +72,7 @@ onBeforeMount(() => {
|
|
|
54
72
|
trailing
|
|
55
73
|
size="xl"
|
|
56
74
|
class="w-full justify-center sm:w-min sm:justify-normal"
|
|
57
|
-
@click="
|
|
75
|
+
@click="toggleCreateNewAccount"
|
|
58
76
|
/>
|
|
59
77
|
<UButton
|
|
60
78
|
v-else
|
|
@@ -68,5 +86,25 @@ onBeforeMount(() => {
|
|
|
68
86
|
:to="rtc.authWebUrl + 'setup-account'"
|
|
69
87
|
/>
|
|
70
88
|
</div>
|
|
89
|
+
|
|
90
|
+
<!-- Create Account Actions -->
|
|
91
|
+
<div v-if="addNew" class="flex justify-end gap-x-3">
|
|
92
|
+
<UButton
|
|
93
|
+
variant="outline"
|
|
94
|
+
:label="$t('connect.label.back')"
|
|
95
|
+
trailing
|
|
96
|
+
size="xl"
|
|
97
|
+
class="w-full justify-center sm:w-min sm:justify-normal"
|
|
98
|
+
@click="toggleCreateNewAccount"
|
|
99
|
+
/>
|
|
100
|
+
<UButton
|
|
101
|
+
:label="$t('connect.label.saveAndContinue')"
|
|
102
|
+
form="account-create-form"
|
|
103
|
+
class="w-full justify-center sm:w-min sm:justify-normal"
|
|
104
|
+
trailing
|
|
105
|
+
type="submit"
|
|
106
|
+
size="xl"
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
71
109
|
</UContainer>
|
|
72
110
|
</template>
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { getAccountCreateSchema } from '#auth/app/utils/schemas/account'
|
|
2
|
+
import type { AccountProfileSchema } from '#auth/app/utils/schemas/account'
|
|
3
|
+
|
|
1
4
|
export const useConnectAccountStore = defineStore('connect-auth-account-store', () => {
|
|
2
5
|
const { $authApi } = useNuxtApp()
|
|
3
6
|
const authApi = useAuthApi()
|
|
@@ -12,6 +15,8 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
|
|
|
12
15
|
const userFirstName = ref<string>(user.value?.firstName || '-')
|
|
13
16
|
const userLastName = ref<string>(user.value?.lastName || '')
|
|
14
17
|
const userFullName = computed(() => `${userFirstName.value} ${userLastName.value}`)
|
|
18
|
+
const createAccountProfileSchema = getAccountCreateSchema()
|
|
19
|
+
const accountFormState = reactive<AccountProfileSchema>(createAccountProfileSchema.parse({}))
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
22
|
* Checks if the current account or the Keycloak user has any of the specified roles.
|
|
@@ -158,9 +163,16 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
|
|
|
158
163
|
pendingApprovalCount.value = 0
|
|
159
164
|
userFirstName.value = user.value?.firstName || '-'
|
|
160
165
|
userLastName.value = user.value?.lastName || ''
|
|
166
|
+
clearAccountState()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function clearAccountState() {
|
|
170
|
+
Object.assign(accountFormState, createAccountProfileSchema.parse({}))
|
|
161
171
|
}
|
|
162
172
|
|
|
163
173
|
return {
|
|
174
|
+
accountFormState,
|
|
175
|
+
clearAccountState,
|
|
164
176
|
currentAccount,
|
|
165
177
|
currentAccountName,
|
|
166
178
|
userAccounts,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { getRequiredAddressSchema } from '#forms/app/utils'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Phone schema: country dialing code + local phone + optional extension.
|
|
6
|
+
* - countryCode: E.164 dialing code (e.g., "+1", "+44").
|
|
7
|
+
* - phoneNumber: accepts common local formats (e.g., "(123) 456-7890").
|
|
8
|
+
* - ext: digits only (optional).
|
|
9
|
+
*/
|
|
10
|
+
export function getPhoneSchema() {
|
|
11
|
+
const t = useNuxtApp().$i18n.t
|
|
12
|
+
|
|
13
|
+
return z.object({
|
|
14
|
+
countryIso2: z.string(),
|
|
15
|
+
countryCode: z.string(),
|
|
16
|
+
phoneNumber: z
|
|
17
|
+
.string()
|
|
18
|
+
.regex(/^\(\d{3}\) \d{3}-\d{4}$/, { message: t('connect.validation.phoneNumberFormat') }),
|
|
19
|
+
ext: z
|
|
20
|
+
.string()
|
|
21
|
+
.regex(/^[0-9]*$/, { message: t('connect.validation.phoneExtFormat') })
|
|
22
|
+
.optional()
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Account create schema — single address + name + email + phone
|
|
28
|
+
* Mirrors your .default(...) pattern.
|
|
29
|
+
*/
|
|
30
|
+
export function getAccountCreateSchema() {
|
|
31
|
+
const t = useNuxtApp().$i18n.t
|
|
32
|
+
|
|
33
|
+
return z.object({
|
|
34
|
+
address: getRequiredAddressSchema().default({
|
|
35
|
+
street: '',
|
|
36
|
+
streetAdditional: '',
|
|
37
|
+
region: '',
|
|
38
|
+
city: '',
|
|
39
|
+
postalCode: '',
|
|
40
|
+
country: 'CA',
|
|
41
|
+
locationDescription: ''
|
|
42
|
+
}),
|
|
43
|
+
accountName: z.string().min(1, t('connect.validation.requiredAccountName')).default(''),
|
|
44
|
+
emailAddress: z.string().email().default(''),
|
|
45
|
+
phone: getPhoneSchema().default({
|
|
46
|
+
countryIso2: 'CA',
|
|
47
|
+
countryCode: '1',
|
|
48
|
+
phoneNumber: '',
|
|
49
|
+
ext: ''
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type AccountProfileSchema = z.output<ReturnType<typeof getAccountCreateSchema>>
|
package/i18n/locales/en-CA.ts
CHANGED
|
@@ -5,12 +5,15 @@ export default {
|
|
|
5
5
|
label: {
|
|
6
6
|
accept: 'Accept',
|
|
7
7
|
accountInfo: 'Account Info',
|
|
8
|
+
accountInformation: 'Account Information',
|
|
8
9
|
accountOptionsMenu: 'Account Options Menu',
|
|
9
10
|
accountSettings: 'Account Settings',
|
|
11
|
+
back: 'Back',
|
|
10
12
|
bceid: 'BCeID',
|
|
11
13
|
bcsc: 'BC Services Card',
|
|
12
14
|
createAccount: 'Create Account',
|
|
13
15
|
createNewAccount: 'Create New Account',
|
|
16
|
+
createNewAccountCont: 'Create a new account to continue',
|
|
14
17
|
decline: 'Decline',
|
|
15
18
|
declineTermsOfUse: 'Decline Terms of Use',
|
|
16
19
|
editProfile: 'Edit Profile',
|
|
@@ -21,6 +24,7 @@ export default {
|
|
|
21
24
|
mainMenu: 'Main Menu',
|
|
22
25
|
notifications: 'Notifications',
|
|
23
26
|
notificationsAria: 'Notifications, {count} unread',
|
|
27
|
+
saveAndContinue: 'Save and Continue',
|
|
24
28
|
sbcAccountCreation: 'Service BC Account Creation',
|
|
25
29
|
selectLoginMethod: 'Select log in method',
|
|
26
30
|
switchAccount: 'Switch Account',
|
|
@@ -45,6 +49,19 @@ export default {
|
|
|
45
49
|
termsOfUse: {
|
|
46
50
|
h1: 'Terms of Use',
|
|
47
51
|
title: 'Terms of Use - Service BC Connect'
|
|
52
|
+
},
|
|
53
|
+
createAccount: {
|
|
54
|
+
yourNameLabel: 'Your Name',
|
|
55
|
+
yourNameHelp: 'This is your legal name as it appears on your BC Services Card.',
|
|
56
|
+
accountNameLabel: 'Account Name',
|
|
57
|
+
accountNameHelp: 'This is your default login name.',
|
|
58
|
+
emailLabel: 'Email',
|
|
59
|
+
emailPlaceholder: 'Enter email address',
|
|
60
|
+
phoneLabel: 'Phone',
|
|
61
|
+
phonePlaceholder: 'Enter phone number',
|
|
62
|
+
phoneExtensionLabel: 'Phone extension (Optional)',
|
|
63
|
+
addressLabel: 'Address',
|
|
64
|
+
countryLabel: 'Country'
|
|
48
65
|
}
|
|
49
66
|
},
|
|
50
67
|
sessionExpiry: {
|
|
@@ -69,7 +86,10 @@ export default {
|
|
|
69
86
|
},
|
|
70
87
|
validation: {
|
|
71
88
|
acceptTermsOfUse: 'Please accept the Terms of Use.',
|
|
72
|
-
termsOfUseScrollToBottom: 'Please scroll to the bottom of the page to accept the Terms of Use.'
|
|
89
|
+
termsOfUseScrollToBottom: 'Please scroll to the bottom of the page to accept the Terms of Use.',
|
|
90
|
+
phoneNumberFormat: 'Phone must be in the format (123) 123-1231',
|
|
91
|
+
phoneExtFormat: 'Extension must be digits only',
|
|
92
|
+
requiredAccountName: 'Account name is required'
|
|
73
93
|
}
|
|
74
94
|
}
|
|
75
95
|
}
|
package/nuxt.config.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sbc-connect/nuxt-auth",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"repository": "github:bcgov/connect-nuxt",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
7
7
|
"main": "./nuxt.config.ts",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"keycloak-js": "^26.2.1",
|
|
24
24
|
"pinia": "^3.0.3",
|
|
25
25
|
"pinia-plugin-persistedstate": "^4.5.0",
|
|
26
|
-
"@sbc-connect/nuxt-base": "0.
|
|
26
|
+
"@sbc-connect/nuxt-base": "0.5.0",
|
|
27
|
+
"@sbc-connect/nuxt-forms": "0.3.0"
|
|
27
28
|
},
|
|
28
29
|
"scripts": {
|
|
29
30
|
"preinstall": "npx only-allow pnpm",
|