@sbc-connect/nuxt-auth 0.0.0-test-ld-20260520212549
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/.env.example +35 -0
- package/CHANGELOG.md +495 -0
- package/LICENSE +28 -0
- package/README.md +58 -0
- package/app/app.config.ts +22 -0
- package/app/components/Connect/Account/Create/Name.vue +73 -0
- package/app/components/Connect/Account/Create/index.vue +148 -0
- package/app/components/Connect/Account/Existing/Alert.vue +10 -0
- package/app/components/Connect/Account/Existing/List/Item.vue +54 -0
- package/app/components/Connect/Account/Existing/List/index.vue +30 -0
- package/app/components/Connect/Header/AccountLabel.vue +39 -0
- package/app/components/Connect/Header/AccountOptionsMenu.vue +52 -0
- package/app/components/Connect/Header/Auth.vue +14 -0
- package/app/components/Connect/Header/AuthenticatedOptions.vue +13 -0
- package/app/components/Connect/Header/CreateAccountButton.vue +13 -0
- package/app/components/Connect/Header/LoginMenu.vue +27 -0
- package/app/components/Connect/Header/Notifications.vue +30 -0
- package/app/components/Connect/Header/UnauthenticatedOptions.vue +15 -0
- package/app/components/Connect/Layout/Auth.vue +19 -0
- package/app/components/Connect/Modal/InvalidIdp.vue +55 -0
- package/app/components/Connect/Modal/SessionExpired.vue +107 -0
- package/app/components/Connect/TermsOfUse/Content.vue +28 -0
- package/app/components/Connect/TermsOfUse/Form.vue +101 -0
- package/app/components/HelloWorld/Auth.vue +12 -0
- package/app/composables/useAuthApi.ts +156 -0
- package/app/composables/useConnectAccountFlowRedirect.ts +54 -0
- package/app/composables/useConnectAppConfig.ts +31 -0
- package/app/composables/useConnectAuth.ts +90 -0
- package/app/composables/useConnectAuthModals.ts +76 -0
- package/app/composables/useConnectHeaderOptions.ts +236 -0
- package/app/composables/useConnectLaunchDarkly.ts +250 -0
- package/app/coverage/app/app.config.ts.html +148 -0
- package/app/coverage/app/components/Connect/Account/Create/Name.vue.html +304 -0
- package/app/coverage/app/components/Connect/Account/Create/index.html +131 -0
- package/app/coverage/app/components/Connect/Account/Create/index.vue.html +487 -0
- package/app/coverage/app/components/Connect/Account/Existing/Alert.vue.html +115 -0
- package/app/coverage/app/components/Connect/Account/Existing/List/Item.vue.html +247 -0
- package/app/coverage/app/components/Connect/Account/Existing/List/index.html +131 -0
- package/app/coverage/app/components/Connect/Account/Existing/List/index.vue.html +175 -0
- package/app/coverage/app/components/Connect/Account/Existing/index.html +116 -0
- package/app/coverage/app/components/Connect/Header/AccountLabel.vue.html +202 -0
- package/app/coverage/app/components/Connect/Header/AccountOptionsMenu.vue.html +241 -0
- package/app/coverage/app/components/Connect/Header/Auth.vue.html +127 -0
- package/app/coverage/app/components/Connect/Header/AuthenticatedOptions.vue.html +124 -0
- package/app/coverage/app/components/Connect/Header/CreateAccountButton.vue.html +124 -0
- package/app/coverage/app/components/Connect/Header/LoginMenu.vue.html +166 -0
- package/app/coverage/app/components/Connect/Header/Notifications.vue.html +175 -0
- package/app/coverage/app/components/Connect/Header/UnauthenticatedOptions.vue.html +130 -0
- package/app/coverage/app/components/Connect/Header/index.html +221 -0
- package/app/coverage/app/components/Connect/Layout/Auth.vue.html +142 -0
- package/app/coverage/app/components/Connect/Layout/index.html +116 -0
- package/app/coverage/app/components/Connect/Modal/SessionExpired.vue.html +364 -0
- package/app/coverage/app/components/Connect/Modal/index.html +116 -0
- package/app/coverage/app/components/Connect/TermsOfUse/Content.vue.html +169 -0
- package/app/coverage/app/components/Connect/TermsOfUse/Form.vue.html +388 -0
- package/app/coverage/app/components/Connect/TermsOfUse/index.html +131 -0
- package/app/coverage/app/components/HelloWorld/Auth.vue.html +121 -0
- package/app/coverage/app/components/HelloWorld/index.html +116 -0
- package/app/coverage/app/composables/index.html +191 -0
- package/app/coverage/app/composables/useAuthApi.ts.html +547 -0
- package/app/coverage/app/composables/useConnectAccountFlowRedirect.ts.html +202 -0
- package/app/coverage/app/composables/useConnectAuth.ts.html +352 -0
- package/app/coverage/app/composables/useConnectAuthModals.ts.html +313 -0
- package/app/coverage/app/composables/useConnectHeaderOptions.ts.html +781 -0
- package/app/coverage/app/composables/useConnectLaunchDarkly.ts.html +820 -0
- package/app/coverage/app/enums/account-status.ts.html +112 -0
- package/app/coverage/app/enums/account-type.ts.html +103 -0
- package/app/coverage/app/enums/connect-access-type.ts.html +94 -0
- package/app/coverage/app/enums/connect-auth-storage-key.ts.html +94 -0
- package/app/coverage/app/enums/connect-idp-hint.ts.html +103 -0
- package/app/coverage/app/enums/connect-login-source.ts.html +103 -0
- package/app/coverage/app/enums/connect-payment-method.ts.html +94 -0
- package/app/coverage/app/enums/connect-product-code.ts.html +94 -0
- package/app/coverage/app/enums/connect-product-type.ts.html +94 -0
- package/app/coverage/app/enums/index.html +251 -0
- package/app/coverage/app/enums/user-settings-type.ts.html +100 -0
- package/app/coverage/app/index.html +116 -0
- package/app/coverage/app/interfaces/connect-account-address.ts.html +88 -0
- package/app/coverage/app/interfaces/connect-account.ts.html +154 -0
- package/app/coverage/app/interfaces/connect-auth-contact.ts.html +130 -0
- package/app/coverage/app/interfaces/connect-auth-profile.ts.html +157 -0
- package/app/coverage/app/interfaces/connect-auth-user.ts.html +115 -0
- package/app/coverage/app/interfaces/connect-terms-of-use.ts.html +103 -0
- package/app/coverage/app/interfaces/connect-user-settings.ts.html +115 -0
- package/app/coverage/app/interfaces/index.html +206 -0
- package/app/coverage/app/layouts/ConnectAuth.vue.html +100 -0
- package/app/coverage/app/layouts/index.html +116 -0
- package/app/coverage/app/middleware/01.setup-accounts.global.ts.html +124 -0
- package/app/coverage/app/middleware/02.keycloak-params.global.ts.html +130 -0
- package/app/coverage/app/middleware/03.allowed-idps.global.ts.html +145 -0
- package/app/coverage/app/middleware/connect-auth.ts.html +238 -0
- package/app/coverage/app/middleware/connect-login-page.ts.html +163 -0
- package/app/coverage/app/middleware/index.html +176 -0
- package/app/coverage/app/pages/auth/account/index.html +116 -0
- package/app/coverage/app/pages/auth/account/select.vue.html +424 -0
- package/app/coverage/app/pages/auth/index.html +146 -0
- package/app/coverage/app/pages/auth/login.vue.html +334 -0
- package/app/coverage/app/pages/auth/logout.vue.html +154 -0
- package/app/coverage/app/pages/auth/terms-of-use.vue.html +316 -0
- package/app/coverage/app/plugins/auth-api.ts.html +169 -0
- package/app/coverage/app/plugins/connect-auth.client.ts.html +382 -0
- package/app/coverage/app/plugins/index.html +131 -0
- package/app/coverage/app/stores/connect-account.ts.html +826 -0
- package/app/coverage/app/stores/index.html +116 -0
- package/app/coverage/app/types/connect-valid-idps.ts.html +100 -0
- package/app/coverage/app/types/index.html +116 -0
- package/app/coverage/app/utils/constants/index.html +131 -0
- package/app/coverage/app/utils/constants/index.ts.html +88 -0
- package/app/coverage/app/utils/constants/valid-idps.ts.html +106 -0
- package/app/coverage/app/utils/index.html +146 -0
- package/app/coverage/app/utils/index.ts.html +88 -0
- package/app/coverage/app/utils/resetPiniaStores.ts.html +184 -0
- package/app/coverage/app/utils/schemas/account.ts.html +313 -0
- package/app/coverage/app/utils/schemas/index.html +116 -0
- package/app/coverage/app/utils/setOnBeforeSessionExpired.ts.html +154 -0
- package/app/coverage/base.css +224 -0
- package/app/coverage/block-navigation.js +87 -0
- package/app/coverage/clover.xml +1939 -0
- package/app/coverage/coverage-final.json +63 -0
- package/app/coverage/favicon.png +0 -0
- package/app/coverage/index.html +431 -0
- package/app/coverage/prettify.css +1 -0
- package/app/coverage/prettify.js +2 -0
- package/app/coverage/sort-arrow-sprite.png +0 -0
- package/app/coverage/sorter.js +210 -0
- package/app/enums/account-status.ts +9 -0
- package/app/enums/account-type.ts +6 -0
- package/app/enums/connect-access-type.ts +3 -0
- package/app/enums/connect-auth-storage-key.ts +3 -0
- package/app/enums/connect-idp-hint.ts +6 -0
- package/app/enums/connect-login-source.ts +6 -0
- package/app/enums/connect-payment-method.ts +3 -0
- package/app/enums/connect-preset-type.ts +4 -0
- package/app/enums/connect-product-code.ts +3 -0
- package/app/enums/connect-product-type.ts +3 -0
- package/app/enums/user-settings-type.ts +5 -0
- package/app/interfaces/app-config-shapes.ts +40 -0
- package/app/interfaces/connect-account-address.ts +3 -0
- package/app/interfaces/connect-account.ts +23 -0
- package/app/interfaces/connect-auth-contact.ts +15 -0
- package/app/interfaces/connect-auth-profile.ts +24 -0
- package/app/interfaces/connect-auth-user.ts +10 -0
- package/app/interfaces/connect-terms-of-use.ts +6 -0
- package/app/interfaces/connect-user-settings.ts +10 -0
- package/app/layouts/ConnectAuth.vue +5 -0
- package/app/middleware/01.setup-accounts.global.ts +13 -0
- package/app/middleware/02.keycloak-params.global.ts +15 -0
- package/app/middleware/03.app-config-presets.global.ts +10 -0
- package/app/middleware/04.idp-enforcement.global.ts +30 -0
- package/app/middleware/connect-auth.ts +72 -0
- package/app/pages/auth/account/select.vue +121 -0
- package/app/pages/auth/login.vue +104 -0
- package/app/pages/auth/logout.vue +23 -0
- package/app/pages/auth/terms-of-use.vue +77 -0
- package/app/plugins/auth-api.ts +28 -0
- package/app/plugins/connect-auth.client.ts +112 -0
- package/app/stores/connect-account.ts +270 -0
- package/app/types/auth-app-config.d.ts +16 -0
- package/app/types/auth-page-meta.d.ts +8 -0
- package/app/types/connect-valid-idps.ts +5 -0
- package/app/utils/constants/index.ts +1 -0
- package/app/utils/constants/valid-idps.ts +7 -0
- package/app/utils/index.ts +1 -0
- package/app/utils/resetPiniaStores.ts +33 -0
- package/app/utils/schemas/account.ts +76 -0
- package/app/utils/setOnBeforeSessionExpired.ts +23 -0
- package/i18n/locales/en-CA.ts +114 -0
- package/i18n/locales/fr-CA.ts +1 -0
- package/nuxt.config.ts +84 -0
- package/package.json +44 -0
- package/playwright-report/index.html +85 -0
- package/public/img/BCReg_Generic_Login_image.jpg +0 -0
- package/test-results/.last-run.json +4 -0
- package/testMocks/auth/index.ts +2 -0
- package/testMocks/auth/profile/index.ts +9 -0
- package/testMocks/auth/profile/json/PUBLIC_USER.json +29 -0
- package/testMocks/auth/settings/index.ts +11 -0
- package/testMocks/auth/settings/json/PREMIUM.json +38 -0
- package/testMocks/auth/settings/json/SBC_STAFF.json +27 -0
- package/testMocks/auth/settings/json/STAFF.json +27 -0
- package/testMocks/mock-helpers.ts +15 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { FormError, InputProps } from '@nuxt/ui'
|
|
3
|
+
|
|
4
|
+
defineProps<{
|
|
5
|
+
error?: FormError<string>
|
|
6
|
+
}>()
|
|
7
|
+
|
|
8
|
+
const authApi = useAuthApi()
|
|
9
|
+
const { accountFormState } = useConnectAccountStore()
|
|
10
|
+
|
|
11
|
+
const isLoading = ref(false)
|
|
12
|
+
const statusCode = defineModel<number | undefined>('statusCode')
|
|
13
|
+
|
|
14
|
+
/** Validate accountName and trigger validation API call */
|
|
15
|
+
const validateName = useDebounceFn(async (accountName: string) => {
|
|
16
|
+
if (accountName.length) {
|
|
17
|
+
isLoading.value = true
|
|
18
|
+
try {
|
|
19
|
+
const { refetch } = authApi.verifyAccountName(accountName)
|
|
20
|
+
const { data } = await refetch()
|
|
21
|
+
statusCode.value = data?.status ?? 500 // fallback as undefined is not considered an exception
|
|
22
|
+
} catch (err: unknown) {
|
|
23
|
+
statusCode.value = getErrorStatus(err) ?? 500
|
|
24
|
+
} finally {
|
|
25
|
+
isLoading.value = false
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
statusCode.value = undefined
|
|
29
|
+
}
|
|
30
|
+
}, 1000)
|
|
31
|
+
|
|
32
|
+
watch(() => accountFormState.accountName, validateName)
|
|
33
|
+
|
|
34
|
+
// Compute UInput props based on loading state and status code
|
|
35
|
+
const uInputProps = computed<InputProps>(() => {
|
|
36
|
+
const iconMap: Record<number, { class: string, name: string }> = {
|
|
37
|
+
204: { class: 'text-success size-7', name: 'i-mdi-check' },
|
|
38
|
+
200: { class: 'text-error size-7', name: 'i-mdi-close' },
|
|
39
|
+
500: { class: 'text-error size-7', name: 'i-mdi-close' }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const icon = statusCode.value ? iconMap[statusCode.value] : null
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
loading: isLoading.value,
|
|
46
|
+
trailing: true,
|
|
47
|
+
ui: {
|
|
48
|
+
trailingIcon: icon?.class || 'text-primary size-7'
|
|
49
|
+
},
|
|
50
|
+
trailingIcon: icon?.name
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// Provide custom props for UInput
|
|
55
|
+
provide('UInput-props-account-name-input', uInputProps)
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<ConnectFormFieldWrapper
|
|
60
|
+
:label="$t('connect.page.createAccount.accountNameLabel')"
|
|
61
|
+
orientation="horizontal"
|
|
62
|
+
:error
|
|
63
|
+
padding-class="pt-6 pb-4 px-4 sm:pb-4 sm:pt-8 sm:px-8"
|
|
64
|
+
>
|
|
65
|
+
<ConnectFormInput
|
|
66
|
+
v-model="accountFormState.accountName"
|
|
67
|
+
name="accountName"
|
|
68
|
+
input-id="account-name-input"
|
|
69
|
+
:label="$t('connect.page.createAccount.accountNameLabel')"
|
|
70
|
+
:help="$t('connect.page.createAccount.accountNameHelp')"
|
|
71
|
+
/>
|
|
72
|
+
</ConnectFormFieldWrapper>
|
|
73
|
+
</template>
|
|
@@ -0,0 +1,148 @@
|
|
|
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 statusCode = ref<number | undefined>(undefined)
|
|
7
|
+
|
|
8
|
+
const accountStore = useConnectAccountStore()
|
|
9
|
+
const { accountFormState, userFullName } = storeToRefs(accountStore)
|
|
10
|
+
const { submitCreateAccount } = accountStore
|
|
11
|
+
const accountProfileSchema = computed(() => getAccountCreateSchema(statusCode.value))
|
|
12
|
+
const formRef = useTemplateRef<Form<AccountProfileSchema>>('account-create-form')
|
|
13
|
+
|
|
14
|
+
const formErrors = computed<{
|
|
15
|
+
accountName: FormError<string> | undefined
|
|
16
|
+
emailAddress: FormError<string> | undefined
|
|
17
|
+
phone: FormError<string> | undefined
|
|
18
|
+
address: FormError<string> | undefined
|
|
19
|
+
}>(() => {
|
|
20
|
+
const errors = formRef.value?.getErrors()
|
|
21
|
+
return {
|
|
22
|
+
accountName: errors?.find(e => e.name === 'accountName'),
|
|
23
|
+
emailAddress: errors?.find(e => e.name?.startsWith('emailAddress')),
|
|
24
|
+
phone: errors?.find(e => e.name?.startsWith('phone')),
|
|
25
|
+
address: errors?.find(e => e.name?.startsWith('address'))
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
async function validate(fieldName?: keyof AccountProfileSchema) {
|
|
30
|
+
return formRef.value?.validate({
|
|
31
|
+
name: fieldName as never,
|
|
32
|
+
silent: true
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
watch(() => statusCode.value, async () => {
|
|
37
|
+
if (statusCode.value !== undefined) {
|
|
38
|
+
await nextTick()
|
|
39
|
+
await validate('accountName')
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<ConnectPageSection
|
|
46
|
+
:heading="{
|
|
47
|
+
label: $t('connect.label.accountInformation'),
|
|
48
|
+
labelClass: 'font-bold md:ml-4',
|
|
49
|
+
}"
|
|
50
|
+
ui-body="p-0 sm:p-0"
|
|
51
|
+
>
|
|
52
|
+
<UForm
|
|
53
|
+
id="account-create-form"
|
|
54
|
+
ref="account-create-form"
|
|
55
|
+
no-validate
|
|
56
|
+
:validate-on="['input', 'change']"
|
|
57
|
+
:schema="accountProfileSchema"
|
|
58
|
+
:state="accountFormState"
|
|
59
|
+
@error="onFormSubmitError"
|
|
60
|
+
@submit="submitCreateAccount()"
|
|
61
|
+
>
|
|
62
|
+
<!-- Legal Name -->
|
|
63
|
+
<ConnectFormFieldWrapper
|
|
64
|
+
:label="$t('connect.page.createAccount.contactLabel')"
|
|
65
|
+
orientation="horizontal"
|
|
66
|
+
class="py-6 sm:py-8"
|
|
67
|
+
>
|
|
68
|
+
<p class="font-bold">
|
|
69
|
+
{{ userFullName }}
|
|
70
|
+
</p>
|
|
71
|
+
<p class="mt-3">
|
|
72
|
+
{{ $t('connect.page.createAccount.yourNameHelp') }}
|
|
73
|
+
</p>
|
|
74
|
+
</ConnectFormFieldWrapper>
|
|
75
|
+
|
|
76
|
+
<USeparator orientation="horizontal" />
|
|
77
|
+
|
|
78
|
+
<!-- Account Name -->
|
|
79
|
+
<ConnectAccountCreateName
|
|
80
|
+
v-model:status-code="statusCode"
|
|
81
|
+
:error="formErrors.accountName"
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
<!-- Account Email -->
|
|
85
|
+
<ConnectFormFieldWrapper
|
|
86
|
+
:label="$t('connect.page.createAccount.emailLabel')"
|
|
87
|
+
orientation="horizontal"
|
|
88
|
+
:error="formErrors.emailAddress"
|
|
89
|
+
padding-class="py-1 px-4 sm:py-2 sm:px-8"
|
|
90
|
+
>
|
|
91
|
+
<ConnectFormInput
|
|
92
|
+
v-model="accountFormState.emailAddress"
|
|
93
|
+
name="emailAddress"
|
|
94
|
+
input-id="email-input"
|
|
95
|
+
:label="$t('connect.page.createAccount.emailPlaceholder')"
|
|
96
|
+
/>
|
|
97
|
+
</ConnectFormFieldWrapper>
|
|
98
|
+
|
|
99
|
+
<!-- Account Phone -->
|
|
100
|
+
<ConnectFieldset
|
|
101
|
+
:label="$t('connect.page.createAccount.phoneLabel')"
|
|
102
|
+
orientation="horizontal"
|
|
103
|
+
:error="formErrors.phone"
|
|
104
|
+
padding-class="py-1 px-4 sm:py-2 sm:px-8"
|
|
105
|
+
>
|
|
106
|
+
<div class="flex flex-row gap-2">
|
|
107
|
+
<!-- Disabling country code selection until Auth Model Supports individual property -->
|
|
108
|
+
<!-- <ConnectFormPhoneCountryCode -->
|
|
109
|
+
<!-- v-model:country-calling-code="accountFormState.phone.countryCode" -->
|
|
110
|
+
<!-- v-model:country-iso2="accountFormState.phone.countryIso2" -->
|
|
111
|
+
<!-- :is-invalid="!accountFormState.phone.countryIso2" -->
|
|
112
|
+
<!-- class="w-40 mt-[-20px]" -->
|
|
113
|
+
<!-- /> -->
|
|
114
|
+
<ConnectFormInput
|
|
115
|
+
v-model="accountFormState.phone.phoneNumber"
|
|
116
|
+
name="phone.phoneNumber"
|
|
117
|
+
input-id="phone-number-input"
|
|
118
|
+
class="flex-2"
|
|
119
|
+
:label="$t('connect.page.createAccount.phonePlaceholder')"
|
|
120
|
+
mask="(###) ###-####"
|
|
121
|
+
/>
|
|
122
|
+
<ConnectFormInput
|
|
123
|
+
v-model="accountFormState.phone.ext"
|
|
124
|
+
name="phone.ext"
|
|
125
|
+
input-id="phone-ext-input"
|
|
126
|
+
:label="$t('connect.page.createAccount.phoneExtensionLabel')"
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
</ConnectFieldset>
|
|
130
|
+
|
|
131
|
+
<!-- Account Address -->
|
|
132
|
+
<ConnectFieldset
|
|
133
|
+
:label="$t('connect.page.createAccount.addressLabel')"
|
|
134
|
+
orientation="horizontal"
|
|
135
|
+
:error="formErrors.address"
|
|
136
|
+
padding-class="py-1 px-4 sm:pt-2 sm:pb-4 sm:px-8"
|
|
137
|
+
>
|
|
138
|
+
<ConnectFormAddress
|
|
139
|
+
id="account-address"
|
|
140
|
+
v-model="accountFormState.address"
|
|
141
|
+
name="address"
|
|
142
|
+
schema-prefix="address"
|
|
143
|
+
@should-validate="validate"
|
|
144
|
+
/>
|
|
145
|
+
</ConnectFieldset>
|
|
146
|
+
</UForm>
|
|
147
|
+
</ConnectPageSection>
|
|
148
|
+
</template>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
account: ConnectAccount
|
|
4
|
+
}>()
|
|
5
|
+
|
|
6
|
+
defineEmits<{
|
|
7
|
+
select: [id: number]
|
|
8
|
+
}>()
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<li class="flex flex-col items-start justify-between gap-4 py-8 first:pt-0 last:pb-0 sm:flex-row sm:items-center">
|
|
13
|
+
<div
|
|
14
|
+
class="flex flex-row items-center gap-4 sm:gap-6"
|
|
15
|
+
:class="{
|
|
16
|
+
'opacity-50': account.accountStatus !== AccountStatus.ACTIVE,
|
|
17
|
+
}"
|
|
18
|
+
>
|
|
19
|
+
<UAvatar
|
|
20
|
+
:alt="account.label[0]"
|
|
21
|
+
:ui="{
|
|
22
|
+
root: 'bg-blue-300 rounded-sm',
|
|
23
|
+
fallback: 'text-white font-bold text-xl',
|
|
24
|
+
}"
|
|
25
|
+
/>
|
|
26
|
+
<div class="flex w-full flex-col text-left">
|
|
27
|
+
<span class="text-lg font-bold text-neutral-highlighted">
|
|
28
|
+
{{ account.label }}
|
|
29
|
+
</span>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="flex w-full flex-col gap-4 sm:w-fit sm:flex-row">
|
|
33
|
+
<div class="my-auto flex gap-2">
|
|
34
|
+
<UBadge
|
|
35
|
+
v-if="account.accountStatus !== AccountStatus.ACTIVE"
|
|
36
|
+
:label="$t('badge.inactiveAccount')"
|
|
37
|
+
class="bg-[#fff7e3] px-3 text-center font-bold text-neutral"
|
|
38
|
+
/>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<UButton
|
|
42
|
+
:label="$t('connect.label.useThisAccount')"
|
|
43
|
+
:aria-label="$t('connect.label.useThisAccountAria', { name: account.label })"
|
|
44
|
+
:icon="'i-mdi-chevron-right'"
|
|
45
|
+
trailing
|
|
46
|
+
:disabled="account.accountStatus !== AccountStatus.ACTIVE"
|
|
47
|
+
size="xl"
|
|
48
|
+
data-testid="choose-existing-account-button"
|
|
49
|
+
class="w-full justify-center sm:w-min sm:justify-normal"
|
|
50
|
+
@click="$emit('select', account.id)"
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
</li>
|
|
54
|
+
</template>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
accounts: ConnectAccount[]
|
|
4
|
+
}>()
|
|
5
|
+
|
|
6
|
+
defineEmits<{
|
|
7
|
+
select: [id: number]
|
|
8
|
+
}>()
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<section class="space-y-4">
|
|
13
|
+
<ConnectI18nHelper
|
|
14
|
+
as="h2"
|
|
15
|
+
class="font-normal"
|
|
16
|
+
translation-path="connect.label.yourExistingAccounts"
|
|
17
|
+
:count="accounts.length"
|
|
18
|
+
/>
|
|
19
|
+
<ul
|
|
20
|
+
class="bg-white flex flex-col divide-y divide-line-muted rounded p-8 max-h-[50dvh] overflow-y-auto"
|
|
21
|
+
>
|
|
22
|
+
<ConnectAccountExistingListItem
|
|
23
|
+
v-for="account in accounts"
|
|
24
|
+
:key="account.id"
|
|
25
|
+
:account
|
|
26
|
+
@select="$emit('select', $event)"
|
|
27
|
+
/>
|
|
28
|
+
</ul>
|
|
29
|
+
</section>
|
|
30
|
+
</template>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const {
|
|
3
|
+
accountName = 'N/A',
|
|
4
|
+
username = 'N/A',
|
|
5
|
+
theme = 'header'
|
|
6
|
+
} = defineProps<{
|
|
7
|
+
accountName?: string
|
|
8
|
+
username?: string
|
|
9
|
+
theme?: 'header' | 'dropdown'
|
|
10
|
+
}>()
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div class="flex items-center gap-1">
|
|
15
|
+
<UAvatar
|
|
16
|
+
:text="username[0] ? username[0].toLocaleUpperCase($i18n.locale) : 'U'"
|
|
17
|
+
size="md"
|
|
18
|
+
class="text-inverted self-start sm:self-center"
|
|
19
|
+
:ui="{ root: 'bg-blue-300 rounded-none text-lg', fallback: 'text-inverted font-bold' }"
|
|
20
|
+
/>
|
|
21
|
+
<div class="mx-2 flex flex-col text-left font-normal tracking-wide">
|
|
22
|
+
<span
|
|
23
|
+
class="text-sm"
|
|
24
|
+
:class="{
|
|
25
|
+
'line-clamp-1 overflow-hidden text-ellipsis text-inverted max-w-64': theme === 'header',
|
|
26
|
+
'text-neutral-highlighted max-w-80': theme === 'dropdown',
|
|
27
|
+
}"
|
|
28
|
+
>
|
|
29
|
+
{{ username }}
|
|
30
|
+
</span>
|
|
31
|
+
<span
|
|
32
|
+
class="line-clamp-1 overflow-hidden text-ellipsis text-xs opacity-75"
|
|
33
|
+
:class="{ 'text-line-muted': theme === 'header', 'text-neutral': theme === 'dropdown' }"
|
|
34
|
+
>
|
|
35
|
+
{{ accountName }}
|
|
36
|
+
</span>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { loggedInUserOptions } = useConnectHeaderOptions()
|
|
3
|
+
const { authUser } = useConnectAuth()
|
|
4
|
+
const accountStore = useConnectAccountStore()
|
|
5
|
+
const isLargeScreen = useMediaQuery('(min-width: 1024px)')
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<UDropdownMenu
|
|
10
|
+
id="account-options-menu"
|
|
11
|
+
:items="loggedInUserOptions"
|
|
12
|
+
:ui="{
|
|
13
|
+
content: 'max-w-[90dvw] account-options-menu-pw-selector',
|
|
14
|
+
}"
|
|
15
|
+
>
|
|
16
|
+
<UButton
|
|
17
|
+
id="account-options-button"
|
|
18
|
+
color="secondary"
|
|
19
|
+
variant="ghost"
|
|
20
|
+
:aria-label="$t('connect.label.accountOptionsMenu')"
|
|
21
|
+
:icon="isLargeScreen ? 'i-mdi-caret-down' : ''"
|
|
22
|
+
trailing
|
|
23
|
+
class="px-2 py-1"
|
|
24
|
+
>
|
|
25
|
+
<ConnectHeaderAccountLabel
|
|
26
|
+
v-if="isLargeScreen"
|
|
27
|
+
:username="parseSpecialCharacters(authUser.fullName, 'USER')"
|
|
28
|
+
:account-name="accountStore.currentAccount.label
|
|
29
|
+
? parseSpecialCharacters(accountStore.currentAccount.label, 'ACCOUNT')
|
|
30
|
+
: ''
|
|
31
|
+
"
|
|
32
|
+
/>
|
|
33
|
+
|
|
34
|
+
<UAvatar
|
|
35
|
+
v-if="!isLargeScreen"
|
|
36
|
+
:text="parseSpecialCharacters(authUser.fullName, 'U')[0]!.toUpperCase()"
|
|
37
|
+
size="md"
|
|
38
|
+
:ui="{ root: 'bg-blue-300 rounded-none text-lg', fallback: 'text-inverted font-bold' }"
|
|
39
|
+
/>
|
|
40
|
+
</UButton>
|
|
41
|
+
<template #account>
|
|
42
|
+
<ConnectHeaderAccountLabel
|
|
43
|
+
:username="parseSpecialCharacters(authUser.fullName, 'USER')"
|
|
44
|
+
:account-name="accountStore.currentAccount.label
|
|
45
|
+
? parseSpecialCharacters(accountStore.currentAccount.label, 'ACCOUNT')
|
|
46
|
+
: ''
|
|
47
|
+
"
|
|
48
|
+
theme="dropdown"
|
|
49
|
+
/>
|
|
50
|
+
</template>
|
|
51
|
+
</UDropdownMenu>
|
|
52
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const ac = useAppConfig().connect.header
|
|
3
|
+
const { isAuthenticated } = useConnectAuth()
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<ConnectHeader>
|
|
8
|
+
<div class="flex gap-4">
|
|
9
|
+
<ConnectHeaderAuthenticatedOptions v-if="isAuthenticated" />
|
|
10
|
+
<ConnectHeaderUnauthenticatedOptions v-else />
|
|
11
|
+
<ConnectLocaleSelect v-if="ac.localeSelect" />
|
|
12
|
+
</div>
|
|
13
|
+
</ConnectHeader>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const ac = useAppConfig().connect.header
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<div
|
|
7
|
+
id="connect-header-authenticated-options"
|
|
8
|
+
class="flex gap-4"
|
|
9
|
+
>
|
|
10
|
+
<ConnectHeaderNotifications v-if="ac.notifications" />
|
|
11
|
+
<ConnectHeaderAccountOptionsMenu v-if="ac.accountOptionsMenu" />
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { createAccountUrl } = useConnectHeaderOptions()
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<UButton
|
|
7
|
+
variant="ghost"
|
|
8
|
+
color="secondary"
|
|
9
|
+
class="px-2 py-1 text-sm"
|
|
10
|
+
:label="$t('connect.label.createAccount')"
|
|
11
|
+
:to="createAccountUrl()"
|
|
12
|
+
/>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useStorage } from '@vueuse/core'
|
|
3
|
+
|
|
4
|
+
const { loggedOutUserOptions, loggedOutUserOptionsMobile } = useConnectHeaderOptions()
|
|
5
|
+
const isLargeScreen = useMediaQuery('(min-width: 1024px)')
|
|
6
|
+
const whatsNew = useStorage<ConnectWhatsNewState>('connect-whats-new', { viewed: false, items: [] })
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<UDropdownMenu
|
|
11
|
+
id="login-menu"
|
|
12
|
+
:items="isLargeScreen ? loggedOutUserOptions : loggedOutUserOptionsMobile"
|
|
13
|
+
>
|
|
14
|
+
<UButton
|
|
15
|
+
variant="ghost"
|
|
16
|
+
color="secondary"
|
|
17
|
+
:label="isLargeScreen ? $t('connect.label.login') : undefined"
|
|
18
|
+
:aria-label="isLargeScreen ? $t('connect.label.selectLoginMethod') : $t('connect.label.mainMenu')"
|
|
19
|
+
:icon="isLargeScreen ? 'i-mdi-caret-down' : 'i-mdi-menu'"
|
|
20
|
+
trailing
|
|
21
|
+
class="px-2 py-1 text-sm"
|
|
22
|
+
/>
|
|
23
|
+
<template #whatsnew-trailing>
|
|
24
|
+
<span v-if="!whatsNew.viewed && whatsNew.items.length > 0" class="size-2 rounded-full bg-error" />
|
|
25
|
+
</template>
|
|
26
|
+
</UDropdownMenu>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { notificationsOptions } = useConnectHeaderOptions()
|
|
3
|
+
const accountStore = useConnectAccountStore()
|
|
4
|
+
const isLargeScreen = useMediaQuery('(min-width: 1024px)')
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<UDropdownMenu :items="notificationsOptions">
|
|
9
|
+
<UChip
|
|
10
|
+
color="error"
|
|
11
|
+
position="top-left"
|
|
12
|
+
inset
|
|
13
|
+
:show="accountStore.pendingApprovalCount > 0"
|
|
14
|
+
>
|
|
15
|
+
<UButton
|
|
16
|
+
variant="ghost"
|
|
17
|
+
color="secondary"
|
|
18
|
+
:label="isLargeScreen ? $t('connect.label.notifications') : undefined"
|
|
19
|
+
:aria-label="$t('connect.label.notificationsAria', { count: accountStore.pendingApprovalCount })"
|
|
20
|
+
:icon="isLargeScreen ? 'i-mdi-caret-down' : ''"
|
|
21
|
+
trailing
|
|
22
|
+
class="px-2 py-1 text-sm"
|
|
23
|
+
>
|
|
24
|
+
<template #leading>
|
|
25
|
+
<UIcon name="i-mdi-bell-outline" class="size-6 shrink-0" />
|
|
26
|
+
</template>
|
|
27
|
+
</UButton>
|
|
28
|
+
</UChip>
|
|
29
|
+
</UDropdownMenu>
|
|
30
|
+
</template>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const ac = useAppConfig().connect.header
|
|
3
|
+
const isLargeScreen = useMediaQuery('(min-width: 1024px)')
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<div
|
|
8
|
+
id="connect-header-unauthenticated-options"
|
|
9
|
+
class="flex gap-4"
|
|
10
|
+
>
|
|
11
|
+
<ConnectHeaderWhatsNew v-if="ac.whatsNew && isLargeScreen" />
|
|
12
|
+
<ConnectHeaderLoginMenu v-if="ac.loginMenu" />
|
|
13
|
+
<ConnectHeaderCreateAccountButton v-if="ac.createAccount && isLargeScreen" />
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const accountStore = useConnectAccountStore()
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<ConnectLayout>
|
|
7
|
+
<template #header>
|
|
8
|
+
<ConnectHeaderAuth />
|
|
9
|
+
</template>
|
|
10
|
+
<template #breadcrumb>
|
|
11
|
+
<ConnectBreadcrumb
|
|
12
|
+
:account-id="accountStore.currentAccount.id
|
|
13
|
+
? String(accountStore.currentAccount.id)
|
|
14
|
+
: undefined"
|
|
15
|
+
/>
|
|
16
|
+
</template>
|
|
17
|
+
<slot />
|
|
18
|
+
</ConnectLayout>
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
currentIdp: ConnectLoginSource
|
|
4
|
+
redirectUrl: string
|
|
5
|
+
}>()
|
|
6
|
+
|
|
7
|
+
const { t, locale } = useI18n()
|
|
8
|
+
|
|
9
|
+
const formattedIdps = computed(() => {
|
|
10
|
+
const allowedIdps = useAppConfig().connect.login.idps
|
|
11
|
+
const labels = allowedIdps.map(idp => t(`connect.label.${idp}`))
|
|
12
|
+
|
|
13
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat#using_format
|
|
14
|
+
const formatter = new Intl.ListFormat(locale.value, {
|
|
15
|
+
style: 'long',
|
|
16
|
+
type: 'disjunction'
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
return formatter.format(labels)
|
|
20
|
+
})
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<UModal
|
|
25
|
+
id="invalid-idp-dialog"
|
|
26
|
+
overlay
|
|
27
|
+
:dismissible="false"
|
|
28
|
+
>
|
|
29
|
+
<template #content>
|
|
30
|
+
<div class="p-10 flex flex-col gap-6">
|
|
31
|
+
<div role="alert">
|
|
32
|
+
<h2
|
|
33
|
+
id="invalid-idp-title"
|
|
34
|
+
class="text-xl font-bold text-neutral-highlighted"
|
|
35
|
+
>
|
|
36
|
+
{{ $t('connect.invalidIdp.title', { idp: currentIdp }) }}
|
|
37
|
+
</h2>
|
|
38
|
+
</div>
|
|
39
|
+
<div>
|
|
40
|
+
<div role="alert">
|
|
41
|
+
<span>{{ $t('connect.invalidIdp.content', { allowedIdps: formattedIdps }) }}</span>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="flex flex-wrap items-center justify-center gap-4">
|
|
45
|
+
<UButton
|
|
46
|
+
:label="$t('connect.label.logout')"
|
|
47
|
+
size="xl"
|
|
48
|
+
class="font-bold"
|
|
49
|
+
@click="useConnectAuth().logout(redirectUrl)"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
</UModal>
|
|
55
|
+
</template>
|