Skip to content

Commit

Permalink
Merge pull request #851 from gobitfly/BEDS-324/Notification-Table-Cli…
Browse files Browse the repository at this point in the history
…ents-Tab

feat: add NotificationsClientTable component with store and API integ…
  • Loading branch information
benji-bitfly authored Sep 16, 2024
2 parents 2c55bd2 + 45380c2 commit 9d1b6fe
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 32 deletions.
189 changes: 189 additions & 0 deletions frontend/components/notifications/NotificationsClientsTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<script setup lang="ts">
import { useNotificationsClientStore } from '~/stores/notifications/useNotificationsClientsStore'
import type { Cursor } from '~/types/datatable'
defineEmits<{ (e: 'openDialog'): void }>()
const cursor = ref<Cursor>()
const pageSize = ref<number>(10)
const { t: $t } = useTranslation()
const {
clientsNotifications,
isLoading,
onSort,
query,
setCursor,
setPageSize,
setSearch,
} = useNotificationsClientStore()
const colsVisible = computed(() => {
return {
footer: 1024,
}
})
const clientLink = computed(() => {
// TODO: implement client link when it's available from the API
// TODO: Test the endpoints
// return `/client/${data.clientId}`
return '/notifications'
})
</script>

<template>
<div>
<BcTableControl
:title="$t('notifications.clients.title')"
:search-placeholder="$t('notifications.clients.search_placeholder')"
@set-search="setSearch"
>
<template #table>
<ClientOnly fallback-tag="span">
<BcTable
:data="clientsNotifications"
data-key="notification_id"
:cursor
:page-size
:selected-sort="query?.sort"
:loading="isLoading"
@set-cursor="setCursor"
@sort="onSort"
@set-page-size="setPageSize"
>
<Column
sortable
header-class="col-client-name"
body-class="col-client-name"
:header="$t('notifications.clients.col.client_name')"
>
<template #body="slotProps">
{{ slotProps.data.client_name }}
</template>
</Column>
<Column
header-class="col-version"
body-class="col-version"
:header="$t('notifications.clients.col.version')"
>
<template #body="slotProps">
<BcLink
:to="clientLink"
class="link"
target="_blank"
>
{{ slotProps.data.version }}
</BcLink>
</template>
</Column>
<Column
field="timestamp"
sortable
header-class="col-age"
body-class="col-age"
>
<template #header>
<BcTableAgeHeader />
</template>
<template #body="slotProps">
<BcFormatTimePassed
:value="slotProps.data.timestamp"
/>
</template>
</Column>
<template #empty>
<NotificationsDashboardsTableEmpty
v-if="!clientsNotifications?.data.length"
@open-dialog="$emit('openDialog')"
/>
</template>
<!-- TODO: implement number of clients subscriptions -->
<template #bc-table-footer-right>
<template v-if="colsVisible">
{{ $t('notifications.clients.footer.subscriptions', { count: 3 }) }}
</template>
</template>
</BcTable>
</ClientOnly>
</template>
</BcTableControl>
</div>
</template>

<style lang="scss" scoped>
@use "~/assets/css/main.scss";
@use "~/assets/css/utils.scss";
$breakpoint-sm: 630px;
$breakpoint-md: 780px;
$breakpoint-lg: 1024px;
:deep(.col-client-name) {
@include utils.truncate-text;
@media (max-width: $breakpoint-lg) {
@include utils.set-all-width(240px);
}
@media (max-width: $breakpoint-md) {
@include utils.set-all-width(200px);
}
@media (max-width: $breakpoint-sm) {
@include utils.set-all-width(106px);
}
}
:deep(.col-version) {
@include utils.truncate-text;
@media (max-width: $breakpoint-lg) {
@include utils.set-all-width(140px);
}
@media (max-width: $breakpoint-sm) {
@include utils.set-all-width(78px);
}
// *:not([data-pc-section="sort"]) {
// }
}
:deep(.col-age) {
@media (max-width: $breakpoint-lg) {
@include utils.set-all-width(140px);
}
@media (max-width: $breakpoint-sm) {
@include utils.set-all-width(78px);
}
*:not([data-pc-section="sort"]) {
@include utils.truncate-text;
}
}
.management-table {
@include main.container;
flex-grow: 1;
display: flex;
flex-direction: column;
overflow-y: hidden;
justify-content: space-between;
:deep(.p-datatable-wrapper) {
flex-grow: 1;
}
}
td > .col-client-name > a{
text-overflow: ellipsis !important;
}
td > .col-client-name {
text-overflow: ellipsis !important;
}
</style>
11 changes: 11 additions & 0 deletions frontend/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,17 @@
"yes": "Yes"
},
"notifications": {
"clients": {
"col": {
"client_name": "Client ",
"version": "Version"
},
"footer":{
"subscriptions": "Clients ({count} Subscriptions)"
},
"search_placeholder":"Client",
"title": "Clients"
},
"col": {
"dashboard": "Dashboard",
"group": "Group",
Expand Down
8 changes: 7 additions & 1 deletion frontend/pages/notifications.vue
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ const openManageNotifications = () => {
/>
</div>
<BcTabList
:tabs default-tab="dashboards"
:tabs
default-tab="dashboards"
:use-route-hash="true"
class="notifications-tab-view"
panels-class="notifications-tab-panels"
Expand All @@ -110,6 +111,11 @@ const openManageNotifications = () => {
@open-dialog="openManageNotifications"
/>
</template>
<template #tab-panel-clients>
<NotificationsClientsTable
@open-dialog="openManageNotifications"
/>
</template>
</BcTabList>
</BcPageWrapper>
</div>
Expand Down
31 changes: 0 additions & 31 deletions frontend/public/mock/notifications/managementDashboard.json

This file was deleted.

69 changes: 69 additions & 0 deletions frontend/stores/notifications/useNotificationsClientsStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { defineStore } from 'pinia'
import type { InternalGetUserNotificationClientsResponse } from '~/types/api/notifications'
import { API_PATH } from '~/types/customFetch'
import type { TableQueryParams } from '~/types/datatable'

const notificationsClientStore = defineStore('notifications-clients-store', () => {
const data = ref<InternalGetUserNotificationClientsResponse | undefined>()
return { data }
})

export function useNotificationsClientStore() {
const { isLoggedIn } = useUserStore()

const { fetch } = useCustomFetch()
const { data } = storeToRefs(notificationsClientStore())
const {
cursor, isStoredQuery, onSort, pageSize, pendingQuery, query, setCursor, setPageSize, setSearch, setStoredQuery,
} = useTableQuery({
limit: 10, sort: 'timestamp:desc',
}, 10)
const isLoading = ref(false)

async function loadClientsNotifications(q: TableQueryParams) {
isLoading.value = true
setStoredQuery(q)
try {
const result = await fetch<InternalGetUserNotificationClientsResponse>(
API_PATH.NOTIFICATIONS_CLIENTS,
undefined,
undefined,
q,
)

isLoading.value = false
if (!isStoredQuery(q)) {
return // in case some query params change while loading
}

data.value = result
}
catch (e) {
data.value = undefined
isLoading.value = false
}
return data.value
}

const clientsNotifications = computed(() => {
return data.value
})

watch(query, (q) => {
if (q) {
isLoggedIn.value && loadClientsNotifications(q)
}
}, { immediate: true })

return {
clientsNotifications,
cursor,
isLoading,
onSort,
pageSize,
query: pendingQuery,
setCursor,
setPageSize,
setSearch,
}
}
5 changes: 5 additions & 0 deletions frontend/types/customFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export enum API_PATH {
LATEST_STATE = '/latestState',
LOGIN = '/login',
LOGOUT = '/logout',
NOTIFICATIONS_CLIENTS = '/notifications/clients',
NOTIFICATIONS_DASHBOARDS = '/notifications/dashboards',
NOTIFICATIONS_MACHINE = '/notifications/machines',
NOTIFICATIONS_MANAGEMENT_GENERAL = '/notifications/managementGeneral',
Expand Down Expand Up @@ -279,6 +280,10 @@ export const mapping: Record<string, MappingData> = {
mock: false,
path: '/logout',
},
[API_PATH.NOTIFICATIONS_CLIENTS]: {
method: 'GET',
path: '/users/me/notifications/clients',
},
[API_PATH.NOTIFICATIONS_DASHBOARDS]: {
path: '/users/me/notifications/dashboards',
},
Expand Down

0 comments on commit 9d1b6fe

Please sign in to comment.