Skip to content

Commit

Permalink
add notification menu to header (#111)
Browse files Browse the repository at this point in the history
* add notification menu to header

* fixing lints

* fixing lints

* add notification intercept

* fix a11y
  • Loading branch information
BrandonSharratt authored Dec 12, 2024
1 parent 6ad0688 commit a3d0c6d
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 8 deletions.
3 changes: 3 additions & 0 deletions cypress/fixtures/notifications.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"count": 1
}
1 change: 1 addition & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ Cypress.Commands.add('visitBusinessDash',
cy.interceptAffiliationRequests(
hasAffiliationInvitations, hasAffiliationInvitationError).as('getAffiliationRequests')
cy.interceptTasks(taskFixture).as('getTasks')
cy.intercept('GET', '**/api/v1/users/**/notifications', { fixture: 'notifications.json' }).as('getNotifications')

cy.visit(`/${identifier}`)
cy.wait([
Expand Down
38 changes: 36 additions & 2 deletions src/components/bcros/header/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@
id="bcros-main-header__container__actions__menus"
class="flex flex-auto justify-end h-full text-white"
>
<div v-if="authenticated" class="flex flex-wrap self-center text-sm">
<BcrosHeaderMenu
data-cy="notification-menu"
:menu-button-text="$t('text.general.notifications')"
:menu-lists="[{header: $t('text.general.notifications'), items: notificationItems}]"
>
<template #menu-button-text>
<UChip color="red" position="top-right" :show="pendingApprovalCount > 0" :ui="{ base: 'ring-0'}">
<UIcon :alt="$t('text.general.notifications')" name="i-mdi-bell-outline" class="mr-2 text-2xl" />
<span class="hidden xl:flex">{{ $t('text.general.notifications') }}</span>
</UChip>
</template>
</BcrosHeaderMenu>
</div>

<div v-if="authenticated" class="flex flex-wrap self-center text-sm">
<BcrosHeaderMenu data-cy="logged-in-menu" :menu-lists="loggedInMenuOptions">
<template #menu-button-text>
Expand Down Expand Up @@ -76,11 +91,12 @@ const {
goToBcrosLogin,
goToEditProfile,
goToTeamMembers,
goToTransactions
goToTransactions,
redirect
} = useBcrosNavigate()
// account / user
const account = useBcrosAccount()
const { currentAccount, currentAccountName, userFullName, userAccounts } = storeToRefs(account)
const { currentAccount, currentAccountName, userFullName, userAccounts, pendingApprovalCount } = storeToRefs(account)
// kc / auth
const keycloak = useBcrosKeycloak()
const authenticated = computed(() => keycloak.kc.authenticated)
Expand Down Expand Up @@ -171,6 +187,24 @@ const switchAccountOptions = computed(() => {
return options
})
const notificationItems = computed((): HeaderMenuItemI[] => {
const label = t('text.general.pendingNotifications', pendingApprovalCount.value)
const authURL = useRuntimeConfig().public.authWebURL
const url = `${authURL}/account/${currentAccount.value.id}/settings/team-members`
const options: HeaderMenuItemI[] = []
const option: HeaderMenuItemI = {
label
}
if (pendingApprovalCount.value > 0) {
option.subLabel = t('text.general.notificationSubLabel', pendingApprovalCount.value)
option.action = () => {
redirect(url)
}
}
options.push(option)
return options
})
const createAccMenuItem = computed((): HeaderMenuItemI[] => {
if ([LoginSourceE.BCROS, LoginSourceE.IDIR].includes(keycloak.kcUser?.loginSource)) {
return []
Expand Down
5 changes: 4 additions & 1 deletion src/components/bcros/header/Menu.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<template>
<Menu as="div" class="relative z-[200]">
<div>
<MenuButton class="flex flex-nowrap content-center p-2 hover:bg-primary-500/[0.2]">
<MenuButton
class="flex flex-nowrap content-center p-2 hover:bg-primary-500/[0.2]"
:aria-label="`${menuButtonText}`"
>
<slot name="menu-button-text">
{{ menuButtonText || '' }}
</slot>
Expand Down
13 changes: 10 additions & 3 deletions src/components/bcros/header/MenuItem.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
<template>
<MenuItem v-slot="{ active }" data-cy="menu-item">
<button
class="flex px-4 py-3 w-full"
:class="{ 'text-primary-500 bg-bcGovGray-100': active || itemInfo.setActive }"
class="px-4 py-3 w-full"
:class="{
'text-primary-500 bg-bcGovGray-100': itemInfo.action && (active || itemInfo.setActive),
'flex': !itemInfo.subLabel,
'hover:cursor-auto': !itemInfo.action
}"
@click="executeAction()"
>
<UIcon v-if="itemInfo.icon" class="text-lg self-center mr-2" :name="itemInfo.icon" data-cy="menu-item-icon" />
<div v-else class="pl-[26px]" data-cy="menu-item-no-icon" />
{{ itemInfo.label }}
<p>{{ itemInfo.label }}</p>
<p v-if="itemInfo.subLabel" class="text-xs opacity-75">
{{ itemInfo.subLabel }}
</p>
</button>
</MenuItem>
</template>
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/header-menu-i.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface HeaderMenuItemI {
args?: any
icon?: string
setActive?: boolean
subLabel?: string
}

export interface HeaderMenuOptionsI {
Expand Down
5 changes: 4 additions & 1 deletion src/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,10 @@
"act": "Act",
"the": "The",
"company": "Company",
"fetchingData": "Loading..."
"fetchingData": "Loading...",
"notifications": "Notifications",
"pendingNotifications": "No notifications | You have {n} pending approval | You have {n} pending approvals",
"notificationSubLabel": "{n} team member require approval to access this account | {n} team members require approval to access this account"
},
"dialog": {
"filing": {
Expand Down
21 changes: 20 additions & 1 deletion src/stores/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export const useBcrosAccount = defineStore('bcros/account', () => {
// api request variables
const apiURL = useRuntimeConfig().public.authApiURL

const pendingApprovalCount: Ref<number> = ref(0)

async function verifyAccountAuthorizations (identifier?: string): Promise<boolean> {
const { trackUiLoadingStart, trackUiLoadingStop } = useBcrosDashboardUi()
trackUiLoadingStart('accountAuthorization')
Expand Down Expand Up @@ -110,6 +112,7 @@ export const useBcrosAccount = defineStore('bcros/account', () => {
/** Get all the current account products. */
async function getAccountProducts (): Promise<ProductI[]> {
const config = { baseURL: apiURL, params: { include_hidden: true } }
fetchPendingApprovalCount().then((count) => { pendingApprovalCount.value = count })
return await useBcrosFetch<ProductI[]>(`orgs/${currentAccount.value?.id}/products`, config)
.then(({ data, error }) => {
if (error.value || !data.value) {
Expand Down Expand Up @@ -179,6 +182,21 @@ export const useBcrosAccount = defineStore('bcros/account', () => {
sessionStorage.setItem(SessionStorageKeyE.CURRENT_ACCOUNT, JSON.stringify(currentAccount.value))
}

async function fetchPendingApprovalCount (): Promise<number> {
if (currentAccount?.value?.id) {
const accountId = currentAccount.value.id
const currentUserSub = user.value.keycloakGuid
interface NotificationsResponse {
count: number
}
const url = `${apiURL}/users/${currentUserSub}/org/${accountId}/notifications`
const response = await useBcrosFetch<NotificationsResponse>(url, {})
return (response && response.data && response.data.count) || 0
} else {
return 0
}
}

return {
currentAccount,
currentAccountName,
Expand All @@ -193,6 +211,7 @@ export const useBcrosAccount = defineStore('bcros/account', () => {
setAccountInfo,
setActiveProducts,
hasProductAccess,
switchCurrentAccount
switchCurrentAccount,
pendingApprovalCount
}
})

0 comments on commit a3d0c6d

Please sign in to comment.