@sbc-connect/nuxt-auth 0.16.7 → 0.17.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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # @sbc-connect/nuxt-auth
2
2
 
3
+ ## 0.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#201](https://github.com/bcgov/connect-nuxt/pull/201) [`0d043ed`](https://github.com/bcgov/connect-nuxt/commit/0d043ed037473ff381e0d3ca17065f11ca3b89d2) Thanks [@sumesh85](https://github.com/sumesh85)! - Account selector: existing-account rows now show the org's mailing address, a payment-method badge, and an NSF/statement-overdue status badge. NSF-suspended accounts are selectable (instead of disabled); clicking one takes the user straight to their account info page instead of into the app.
8
+
9
+ - `ConnectAccount` receives optional `address`, `paymentMethod`, `hasNsfInvoices`, and `hasOverdueInvoices` fields.
10
+ - `ConnectAccountExistingList`/`ConnectAccountExistingListItem` receives `showAddress`, `showPaymentMethodBadge`, and `showStatusBadge` props, all defaulting to `false` — existing consumers see no change on upgrade unless they opt in. The layer's own `/auth/account/select` page opts in to `showStatusBadge` so it keeps showing the NSF indicator.
11
+
12
+ - [#203](https://github.com/bcgov/connect-nuxt/pull/203) [`fedf109`](https://github.com/bcgov/connect-nuxt/commit/fedf109afdefa2869325afb9c7a268767c0e3d38) Thanks [@deetz99](https://github.com/deetz99)! - Update dependencies
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [[`fedf109`](https://github.com/bcgov/connect-nuxt/commit/fedf109afdefa2869325afb9c7a268767c0e3d38), [`fedf109`](https://github.com/bcgov/connect-nuxt/commit/fedf109afdefa2869325afb9c7a268767c0e3d38)]:
17
+ - @sbc-connect/nuxt-base@0.14.0
18
+ - @sbc-connect/nuxt-forms@0.8.0
19
+
3
20
  ## 0.16.7
4
21
 
5
22
  ### Patch Changes
@@ -1,11 +1,51 @@
1
1
  <script setup lang="ts">
2
- defineProps<{
2
+ const {
3
+ account,
4
+ showPaymentMethodBadge = false,
5
+ showStatusBadge = false,
6
+ showAddress = false
7
+ } = defineProps<{
3
8
  account: ConnectAccount
9
+ showPaymentMethodBadge?: boolean
10
+ showStatusBadge?: boolean
11
+ showAddress?: boolean
4
12
  }>()
5
13
 
6
- defineEmits<{
14
+ const emit = defineEmits<{
7
15
  select: [id: number]
8
16
  }>()
17
+
18
+ // ACTIVE is always selectable. Of the non-active statuses, only NSF_SUSPENDED also stays
19
+ // enabled (it redirects below instead of emitting select) - everything else stays disabled.
20
+ const isNsfSuspended = computed(() => account.accountStatus === AccountStatus.NSF_SUSPENDED)
21
+ const isUnusable = computed(() => account.accountStatus !== AccountStatus.ACTIVE && !isNsfSuspended.value)
22
+
23
+ function handleSelect() {
24
+ if (isNsfSuspended.value) {
25
+ useConnectAccountStore().redirectToAccountInfo(account)
26
+ return
27
+ }
28
+ emit('select', account.id)
29
+ }
30
+
31
+ const statusBadgeLabel = computed(() => {
32
+ if (!isNsfSuspended.value) {
33
+ return undefined
34
+ }
35
+ if (account.hasOverdueInvoices) {
36
+ return 'connect.badge.statementOverdue'
37
+ }
38
+ return 'connect.badge.nonSufficientFunds'
39
+ })
40
+
41
+ const address = computed(() => account.address)
42
+ const addressLine = computed(() => {
43
+ const addr = address.value
44
+ if (!addr) {
45
+ return undefined
46
+ }
47
+ return [addr.street, addr.city, addr.region, addr.postalCode, addr.country].filter(Boolean).join(', ')
48
+ })
9
49
  </script>
10
50
 
11
51
  <template>
@@ -13,7 +53,7 @@ defineEmits<{
13
53
  <div
14
54
  class="flex flex-row items-center gap-4 sm:gap-6"
15
55
  :class="{
16
- 'opacity-50': account.accountStatus !== AccountStatus.ACTIVE,
56
+ 'opacity-50': isUnusable,
17
57
  }"
18
58
  >
19
59
  <UAvatar
@@ -27,13 +67,31 @@ defineEmits<{
27
67
  <span class="text-lg font-bold text-neutral-highlighted">
28
68
  {{ account.label }}
29
69
  </span>
70
+ <span v-if="showAddress && addressLine" class="text-sm text-neutral">
71
+ {{ addressLine }}
72
+ </span>
73
+ <div class="mt-1 flex flex-wrap gap-2">
74
+ <UBadge
75
+ v-if="showPaymentMethodBadge && account.paymentMethod"
76
+ :label="$t(`connect.badge.paymentMethod.${account.paymentMethod}`)"
77
+ variant="subtle"
78
+ color="neutral"
79
+ class="px-3 text-center font-bold"
80
+ />
81
+ <UBadge
82
+ v-if="showStatusBadge && statusBadgeLabel"
83
+ :label="$t(statusBadgeLabel)"
84
+ color="error"
85
+ class="px-3 text-center font-bold"
86
+ />
87
+ </div>
30
88
  </div>
31
89
  </div>
32
90
  <div class="flex w-full flex-col gap-4 sm:w-fit sm:flex-row">
33
91
  <div class="my-auto flex gap-2">
34
92
  <UBadge
35
- v-if="account.accountStatus !== AccountStatus.ACTIVE"
36
- :label="$t('badge.inactiveAccount')"
93
+ v-if="isUnusable"
94
+ :label="$t('connect.badge.inactiveAccount')"
37
95
  class="bg-[#fff7e3] px-3 text-center font-bold text-neutral"
38
96
  />
39
97
  </div>
@@ -43,11 +101,11 @@ defineEmits<{
43
101
  :aria-label="$t('connect.label.useThisAccountAria', { name: account.label })"
44
102
  :icon="'i-mdi-chevron-right'"
45
103
  trailing
46
- :disabled="account.accountStatus !== AccountStatus.ACTIVE"
104
+ :disabled="isUnusable"
47
105
  size="xl"
48
106
  data-testid="choose-existing-account-button"
49
107
  class="w-full justify-center sm:w-min sm:justify-normal"
50
- @click="$emit('select', account.id)"
108
+ @click="handleSelect"
51
109
  />
52
110
  </div>
53
111
  </li>
@@ -1,6 +1,14 @@
1
1
  <script setup lang="ts">
2
- defineProps<{
2
+ const {
3
+ accounts,
4
+ showPaymentMethodBadge = false,
5
+ showStatusBadge = false,
6
+ showAddress = false
7
+ } = defineProps<{
3
8
  accounts: ConnectAccount[]
9
+ showPaymentMethodBadge?: boolean
10
+ showStatusBadge?: boolean
11
+ showAddress?: boolean
4
12
  }>()
5
13
 
6
14
  defineEmits<{
@@ -23,6 +31,9 @@ defineEmits<{
23
31
  v-for="account in accounts"
24
32
  :key="account.id"
25
33
  :account
34
+ :show-payment-method-badge="showPaymentMethodBadge"
35
+ :show-status-badge="showStatusBadge"
36
+ :show-address="showAddress"
26
37
  @select="$emit('select', $event)"
27
38
  />
28
39
  </ul>
@@ -7,6 +7,10 @@ export interface ConnectAccount {
7
7
  type: UserSettingsType.ACCOUNT
8
8
  urlpath: string
9
9
  urlorigin: string
10
+ address?: ConnectAccountAddress
11
+ paymentMethod?: string
12
+ hasNsfInvoices?: string
13
+ hasOverdueInvoices?: string
10
14
  }
11
15
 
12
16
  export interface ConnectCreateAccount {
@@ -7,4 +7,5 @@ export interface ConnectUserSettings {
7
7
  accountStatus?: AccountStatus
8
8
  additionalLabel?: string
9
9
  label?: string
10
+ address?: ConnectAccountAddress
10
11
  }
@@ -90,6 +90,7 @@ onBeforeMount(() => {
90
90
  <ConnectAccountExistingList
91
91
  v-if="showAccountList"
92
92
  :accounts="store.userAccounts"
93
+ :show-status-badge="true"
93
94
  @select="selectAndRedirect"
94
95
  />
95
96
 
@@ -113,7 +114,7 @@ onBeforeMount(() => {
113
114
  trailing
114
115
  size="xl"
115
116
  class="w-full justify-center sm:w-min sm:justify-normal"
116
- @click="addNew = true"
117
+ @click="() => { addNew = true }"
117
118
  />
118
119
  <UButton
119
120
  v-else
@@ -142,7 +143,7 @@ onBeforeMount(() => {
142
143
  trailing
143
144
  size="xl"
144
145
  class="w-full justify-center sm:w-min sm:justify-normal"
145
- @click="addNew = false"
146
+ @click="() => { addNew = false }"
146
147
  />
147
148
  <UButton
148
149
  :label="$t('connect.label.saveAndContinue')"
@@ -2,7 +2,7 @@ export default defineNuxtPlugin({
2
2
  name: 'auth-api',
3
3
  order: -20,
4
4
  parallel: true,
5
- setup(nuxtApp) {
5
+ setup(nuxtApp): { provide: { authApi: ReturnType<typeof $fetch.create> } } {
6
6
  const rtc = nuxtApp.$config.public
7
7
  const authApiUrl = rtc.authApiUrl + rtc.authApiVersion
8
8
  const appName = rtc.appName
@@ -5,7 +5,7 @@ export default defineNuxtPlugin({
5
5
  name: 'connect-auth',
6
6
  order: -20,
7
7
  parallel: true,
8
- async setup(nuxtApp) {
8
+ async setup(nuxtApp): Promise<{ provide: { connectAuth: Keycloak } }> {
9
9
  const rtc = nuxtApp.$config.public
10
10
 
11
11
  // define new keycloak
@@ -62,7 +62,7 @@ export const useConnectAuthQuery = () => {
62
62
  function userSettingsOptions(options?: DefineOptions<ConnectUserSettings[]>) {
63
63
  const keycloakGuid = useConnectAuth().authUser.value?.keycloakGuid
64
64
  return defineQueryOptions({
65
- query: () => $authApi<ConnectUserSettings[]>(`/users/${keycloakGuid}/settings`),
65
+ query: () => $authApi<ConnectUserSettings[]>(`/users/${keycloakGuid}/settings`, { query: { expand: 'address' } }),
66
66
  staleTime: DEFAULT_STALE_TIME,
67
67
  enabled: !!keycloakGuid,
68
68
  ...options,
@@ -74,12 +74,21 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
74
74
  const accounts = await service.getUserAccounts(force).catch(() => undefined)
75
75
  if (accounts && accounts[0]) {
76
76
  userAccounts.value = accounts
77
- if (!currentAccount.value.id || !userAccounts.value.some(account => account.id === currentAccount.value.id)) {
78
- currentAccount.value = accounts[0]
79
- }
77
+ // Always resync from the fresh list so a persisted currentAccount never keeps a stale status.
78
+ currentAccount.value = accounts.find(account => account.id === currentAccount.value.id) ?? accounts[0]
80
79
  }
81
80
  }
82
81
 
82
+ /**
83
+ * Sets `account` as current and navigates to its own info/settings page. Used both when a
84
+ * user clicks a visibly NSF/overdue account, and when a consumer's own backend rejects an
85
+ * action (e.g. payment redemption) for an account that wasn't flagged yet on the client.
86
+ */
87
+ function redirectToAccountInfo(account: ConnectAccount) {
88
+ currentAccount.value = account
89
+ return navigateTo(`${account.urlorigin}${account.urlpath}`, { external: true })
90
+ }
91
+
83
92
  /** Switch the current account to the given account ID if it exists in the user's account list */
84
93
  function switchCurrentAccount(accountId: number) {
85
94
  const account = userAccounts.value.find(account => account.id === accountId)
@@ -90,11 +99,16 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
90
99
  }
91
100
 
92
101
  function checkAccountStatus() {
93
- // redirect if account status is suspended or in review
94
- if ([AccountStatus.NSF_SUSPENDED, AccountStatus.SUSPENDED].includes(currentAccount.value?.accountStatus)) {
95
- // Avoid redirecting when navigating back from PAYBC for NSF or signout.
96
- const endPath = useRoute().path.split('/').pop() as string
97
- const isAllowedPath = ['return-cc-payment', 'signout'].includes(endPath)
102
+ const status = currentAccount.value?.accountStatus
103
+ if ([AccountStatus.SUSPENDED, AccountStatus.NSF_SUSPENDED].includes(status)) {
104
+ // Plain SUSPENDED has no self-serve resolution anywhere, so only the base paths are
105
+ // exempt. NSF_SUSPENDED additionally owns its own handling on the account selector and
106
+ // anywhere in a pay-link flow (dynamic /pay/{token}/... routes).
107
+ const path = useRoute().path
108
+ const endPath = path.split('/').pop() as string
109
+ const isNsfException = status === AccountStatus.NSF_SUSPENDED
110
+ && (path.includes('/pay/') || ['select', 'create'].includes(endPath))
111
+ const isAllowedPath = ['return-cc-payment', 'signout'].includes(endPath) || isNsfException
98
112
  if (!isAllowedPath) {
99
113
  // URL not allowed so redirect
100
114
  const redirectUrl = `${rtc.authWebUrl}account-freeze`
@@ -150,6 +164,7 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
150
164
  initAccountStore,
151
165
  isCurrentAccount,
152
166
  loadUserAccounts,
167
+ redirectToAccountInfo,
153
168
  syncUserProfile,
154
169
  switchCurrentAccount,
155
170
  userAccounts,
@@ -2,6 +2,22 @@
2
2
  export default {
3
3
  /* Ordering should be alphabetical unless otherwise specified */
4
4
  connect: {
5
+ badge: {
6
+ inactiveAccount: 'Inactive',
7
+ nonSufficientFunds: 'Non-Sufficient Funds',
8
+ paymentMethod: {
9
+ CASH: 'Cash',
10
+ CC: 'Credit Card',
11
+ CHEQUE: 'Cheque',
12
+ DIRECT_PAY: 'Credit Card',
13
+ DRAWDOWN: 'BC Online',
14
+ EFT: 'Electronic Funds Transfer',
15
+ EJV: 'Electronic Journal Voucher',
16
+ ONLINE_BANKING: 'Online Banking',
17
+ PAD: 'Pre-Authorized Debit'
18
+ },
19
+ statementOverdue: 'Statement Overdue'
20
+ },
5
21
  label: {
6
22
  accept: 'Accept',
7
23
  accountInfo: 'Account Info',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sbc-connect/nuxt-auth",
3
3
  "type": "module",
4
- "version": "0.16.7",
4
+ "version": "0.17.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "github:bcgov/connect-nuxt"
@@ -9,24 +9,24 @@
9
9
  "license": "BSD-3-Clause",
10
10
  "main": "./nuxt.config.ts",
11
11
  "devDependencies": {
12
- "@axe-core/playwright": "4.11.3",
13
- "dotenv": "17.4.2",
14
- "nuxt": "4.4.8",
12
+ "@axe-core/playwright": "4.13.0",
13
+ "dotenv": "18.0.4",
14
+ "nuxt": "4.5.2",
15
15
  "typescript": "6.0.3",
16
- "vue-tsc": "3.3.1",
16
+ "vue-tsc": "3.3.11",
17
17
  "@sbc-connect/eslint-config": "0.0.8",
18
- "@sbc-connect/playwright-config": "0.2.1",
19
- "@sbc-connect/vitest-config": "0.2.0"
18
+ "@sbc-connect/vitest-config": "0.2.0",
19
+ "@sbc-connect/playwright-config": "0.2.1"
20
20
  },
21
21
  "dependencies": {
22
- "@pinia/colada": "1.3.0",
23
- "@pinia/colada-nuxt": "1.0.1",
24
- "@pinia/nuxt": "0.11.3",
22
+ "@pinia/colada": "1.4.6",
23
+ "@pinia/colada-nuxt": "1.2.0",
24
+ "@pinia/nuxt": "1.0.2",
25
25
  "keycloak-js": "26.2.4",
26
- "pinia": "3.0.4",
26
+ "pinia": "4.0.3",
27
27
  "pinia-plugin-persistedstate": "4.7.1",
28
- "@sbc-connect/nuxt-base": "0.13.3",
29
- "@sbc-connect/nuxt-forms": "0.7.12"
28
+ "@sbc-connect/nuxt-base": "0.14.0",
29
+ "@sbc-connect/nuxt-forms": "0.8.0"
30
30
  },
31
31
  "scripts": {
32
32
  "preinstall": "npx only-allow pnpm",
@@ -9,7 +9,7 @@ export const mockApiCallsForSetAccount = async (
9
9
  page.route('**/users/@me', async (route) => {
10
10
  await route.fulfill({ json: getUserProfileMock() })
11
11
  })
12
- page.route('**/users/**/settings', async (route) => {
12
+ page.route('**/users/**/settings**', async (route) => {
13
13
  await route.fulfill({ json: getUserSettingsMock(accountType) })
14
14
  })
15
15
  }