@bildvitta/quasar-ui-asteroid 3.14.0-beta.2 → 3.14.0-beta.4

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.
Files changed (31) hide show
  1. package/package.json +1 -1
  2. package/src/components/app-bar/QasAppBar.vue +6 -1
  3. package/src/components/app-bar/QasAppBar.yml +3 -0
  4. package/src/components/app-menu/QasAppMenu.vue +3 -2
  5. package/src/components/app-menu/QasAppMenu.yml +3 -0
  6. package/src/components/app-menu/composables/use-app-user.js +3 -0
  7. package/src/components/app-user/QasAppUser.vue +68 -13
  8. package/src/components/app-user/QasAppUser.yml +3 -0
  9. package/src/components/avatar/QasAvatar.vue +9 -3
  10. package/src/components/avatar/QasAvatar.yml +1 -1
  11. package/src/components/avatar/enums/AvatarColors.js +2 -1
  12. package/src/components/card/QasCard.vue +3 -3
  13. package/src/components/date/QasDate.vue +31 -36
  14. package/src/components/dialog/QasDialog.vue +29 -1
  15. package/src/components/drawer/QasDrawer.vue +117 -0
  16. package/src/components/drawer/QasDrawer.yml +57 -0
  17. package/src/components/infinite-scroll/QasInfiniteScroll.vue +6 -2
  18. package/src/components/infinite-scroll/QasInfiniteScroll.yml +22 -0
  19. package/src/components/layout/QasLayout.vue +83 -52
  20. package/src/components/layout/QasLayout.yml +5 -0
  21. package/src/components/layout/private/PvLayoutNotificationCard.vue +86 -0
  22. package/src/components/layout/private/PvLayoutNotificationsDrawer.vue +141 -0
  23. package/src/components/list-items/QasListItems.vue +33 -5
  24. package/src/components/list-items/QasListItems.yml +5 -0
  25. package/src/components/whatsapp-link/QasWhatsappLink.vue +2 -2
  26. package/src/components/whatsapp-link/QasWhatsappLink.yml +1 -1
  27. package/src/composables/index.js +3 -0
  28. package/src/composables/use-notifications.js +114 -0
  29. package/src/css/plugins/notify.scss +40 -2
  30. package/src/helpers/private/has-parent-by-class-name.js +15 -0
  31. package/src/vue-plugin.js +3 -0
@@ -62,7 +62,7 @@ const props = defineProps({
62
62
 
63
63
  defineExpose({ refresh, remove })
64
64
 
65
- const emit = defineEmits(['update:list'])
65
+ const emit = defineEmits(['update:list', 'fetch-success', 'fetch-error'])
66
66
 
67
67
  const axios = inject('axios')
68
68
 
@@ -135,10 +135,14 @@ async function fetchList () {
135
135
  * após buscar uma vez e retornar uma lista vazia.
136
136
  */
137
137
  hasMadeFirstFetch.value = true
138
- } catch {
138
+
139
+ emit('fetch-success', { list: newList, offset: offset.value, count: count.value })
140
+ } catch (error) {
139
141
  NotifyError('Ops… Não conseguimos acessar as informações. Por favor, tente novamente em alguns minutos.')
140
142
 
141
143
  hasFetchingError.value = true
144
+
145
+ emit('fetch-error', error)
142
146
  } finally {
143
147
  isFetching.value = false
144
148
  }
@@ -44,6 +44,28 @@ events:
44
44
  desc: Novo valor do list
45
45
  type: Array
46
46
 
47
+ '@fetch-error -> function (error)':
48
+ desc: Dispara toda vez que ocorre algum erro ao fazer nova busca na API.
49
+ params:
50
+ error:
51
+ count: Erro enviado da API.
52
+ type: Object
53
+
54
+ '@fetch-success -> function ({ count, list, offset })':
55
+ desc: Dispara toda vez que é feito uma nova busca com sucesso na API.
56
+ params:
57
+ count:
58
+ count: Tamanho máximo de itens.
59
+ type: Number
60
+
61
+ list:
62
+ desc: Lista do endpoint.
63
+ type: Array
64
+
65
+ offset:
66
+ desc: valor atual que indica a posição inicial ao recuperar resultados da lista.
67
+ type: Number
68
+
47
69
  slots:
48
70
  default:
49
71
  desc: slot para exibir a lista na qual o componente fez a busca.
@@ -1,11 +1,11 @@
1
1
  <template>
2
2
  <q-layout view="hHh Lpr lff">
3
3
  <slot v-if="$qas.screen.untilLarge" name="app-bar">
4
- <qas-app-bar v-bind="appBarProps" @sign-out="signOut" @toggle-menu="toggleMenuDrawer" />
4
+ <qas-app-bar v-bind="appBarProps" @sign-out="signOut" @toggle-menu="toggleMenuDrawer" @toggle-notifications="toggleNotificationsDrawer" />
5
5
  </slot>
6
6
 
7
7
  <slot name="app-menu">
8
- <qas-app-menu :model-value="showMenuDrawer" v-bind="defaultAppMenuProps" @sign-out="signOut" @update:model-value="updateMenuDrawer" />
8
+ <qas-app-menu :model-value="showMenuDrawer" v-bind="defaultAppMenuProps" @sign-out="signOut" @toggle-notifications="toggleNotificationsDrawer" @update:model-value="updateMenuDrawer" />
9
9
  </slot>
10
10
 
11
11
  <slot>
@@ -17,72 +17,103 @@
17
17
  </slot>
18
18
 
19
19
  <q-ajax-bar color="primary" position="bottom" size="2px" />
20
+
21
+ <pv-layout-notifications-drawer v-if="isNotificationsEnabled" v-model="notificationsDrawer" />
20
22
  </q-layout>
21
23
  </template>
22
24
 
23
- <script>
25
+ <script setup>
26
+ import PvLayoutNotificationsDrawer from './private/PvLayoutNotificationsDrawer.vue'
24
27
  import QasAppBar from '../app-bar/QasAppBar.vue'
25
28
  import QasAppMenu from '../app-menu/QasAppMenu.vue'
26
29
 
27
- export default {
28
- name: 'QasLayout',
30
+ import useScreen from '../../composables/use-screen'
31
+ import useNotifications from '../../composables/use-notifications'
29
32
 
30
- components: {
31
- QasAppBar,
32
- QasAppMenu
33
- },
33
+ import { computed, ref, watch } from 'vue'
34
34
 
35
- props: {
36
- appBarProps: {
37
- default: () => ({}),
38
- type: Object
39
- },
40
-
41
- appMenuProps: {
42
- default: () => ({}),
43
- type: Object
44
- },
45
-
46
- modelValue: {
47
- default: true,
48
- type: Boolean
49
- }
50
- },
35
+ defineOptions({ name: 'QasLayout' })
51
36
 
52
- emits: ['sign-out', 'update:modelValue'],
37
+ const props = defineProps({
38
+ appBarProps: {
39
+ default: () => ({}),
40
+ type: Object
41
+ },
53
42
 
54
- data () {
55
- return {
56
- menuDrawer: false
57
- }
43
+ appMenuProps: {
44
+ default: () => ({}),
45
+ type: Object
58
46
  },
59
47
 
60
- computed: {
61
- defaultAppMenuProps () {
62
- return {
63
- ...this.appBarProps,
64
- ...this.appMenuProps
65
- }
66
- },
67
-
68
- showMenuDrawer () {
69
- return !this.$qas.screen.untilLarge || this.menuDrawer
70
- }
48
+ initialUnreadNotificationsCount: {
49
+ type: Number,
50
+ default: 0
71
51
  },
72
52
 
73
- methods: {
74
- signOut () {
75
- this.$emit('sign-out')
76
- },
53
+ modelValue: {
54
+ default: true,
55
+ type: Boolean
56
+ }
57
+ })
58
+
59
+ const emit = defineEmits(['sign-out', 'update:modelValue'])
60
+
61
+ // expondo método "toggleNotificationsDrawer" para fora do componente.
62
+ defineExpose({ toggleNotificationsDrawer })
63
+
64
+ const screen = useScreen()
65
+
66
+ const { isNotificationsEnabled, setUnreadNotificationsCount } = useNotifications()
67
+
68
+ const menuDrawer = ref(false)
69
+ const notificationsDrawer = ref(false)
70
+
71
+ /**
72
+ * Como está sendo utilizado em um watcher com a propriedade 'immediate: true',
73
+ * é necessário criar a variável antes de atribuí-la ao watcher, para assim conseguir pará-lo.
74
+ */
75
+ let unreadNotificationsCountWatcher = () => {}
77
76
 
78
- toggleMenuDrawer () {
79
- this.updateMenuDrawer(!this.menuDrawer)
80
- },
77
+ // computed
78
+ const defaultAppMenuProps = computed(() => {
79
+ return {
80
+ ...props.appBarProps,
81
+ ...props.appMenuProps
82
+ }
83
+ })
84
+
85
+ const showMenuDrawer = computed(() => !screen.untilLarge || menuDrawer.value)
86
+
87
+ /**
88
+ * A propriedade "initialUnreadNotificationsCount" é escutada apenas uma vez,
89
+ * quando ela é iniciada, seta o "unreadNotificationsCount" do composable,
90
+ * após isto quem controla é o QasLayout.
91
+ */
92
+ unreadNotificationsCountWatcher = watch(() => props.initialUnreadNotificationsCount, value => {
93
+ if (value) {
94
+ setUnreadNotificationsCount(value)
81
95
 
82
- updateMenuDrawer (value) {
83
- this.menuDrawer = value
84
- this.$emit('update:modelValue', value)
85
- }
96
+ // finaliza o watcher
97
+ unreadNotificationsCountWatcher()
86
98
  }
99
+ }, { immediate: true })
100
+
101
+ // functions
102
+ function signOut () {
103
+ emit('sign-out')
104
+ }
105
+
106
+ function toggleMenuDrawer () {
107
+ updateMenuDrawer(!menuDrawer.value)
108
+ }
109
+
110
+ function updateMenuDrawer (value) {
111
+ menuDrawer.value = value
112
+
113
+ emit('update:modelValue', value)
114
+ }
115
+
116
+ function toggleNotificationsDrawer () {
117
+ notificationsDrawer.value = !notificationsDrawer.value
87
118
  }
88
119
  </script>
@@ -14,6 +14,11 @@ props:
14
14
  default: { animation: 500 }
15
15
  type: Object
16
16
 
17
+ initial-unread-notifications-count:
18
+ desc: Propriedade para indicar quantas notificações não lidas existem.
19
+ default: 0
20
+ type: Number
21
+
17
22
  model-value:
18
23
  desc: Model do componente, responsável por abrir/fechar menu lateral.
19
24
  default: true
@@ -0,0 +1,86 @@
1
+ <template>
2
+ <div class="items-center justify-between no-wrap row">
3
+ <div class="items-center row">
4
+ <div class="q-mr-sm">
5
+ <q-icon :color="iconColor" name="sym_r_info" size="md" />
6
+ </div>
7
+
8
+ <div>
9
+ <span class="text-caption text-grey-6">
10
+ {{ dateLabel }}
11
+ </span>
12
+
13
+ <div class="items-center q-mt-xs row">
14
+ <h6 class="text-subtitle1" :class="titleClass">
15
+ {{ props.notification.title }}
16
+ </h6>
17
+
18
+ <div v-if="hasBadge" class="q-ml-sm">
19
+ <qas-badge color="indigo-1" label="Nova" text-color="grey-10" />
20
+ </div>
21
+ </div>
22
+
23
+ <div class="q-mt-xs text-body1 text-grey-8">
24
+ {{ props.notification.message }}
25
+ </div>
26
+ </div>
27
+ </div>
28
+
29
+ <qas-btn v-if="props.notification.link" v-bind="buttonProps" />
30
+ </div>
31
+ </template>
32
+
33
+ <script setup>
34
+ import { dateTime } from '../../../helpers/filters'
35
+
36
+ import { computed } from 'vue'
37
+ import { date } from 'quasar'
38
+
39
+ defineOptions({ name: 'PvLayoutNotificationCard' })
40
+
41
+ const props = defineProps({
42
+ notification: {
43
+ type: Object,
44
+ default: () => ({})
45
+ }
46
+ })
47
+
48
+ const markedAsRead = computed(() => props.notification.isRead)
49
+
50
+ const iconColor = computed(() => markedAsRead.value ? 'grey-8' : 'primary')
51
+ const titleClass = computed(() => markedAsRead.value ? 'text-grey-8' : 'text-grey-10')
52
+
53
+ const isRecentNotification = computed(() => {
54
+ const currentDate = new Date().toISOString()
55
+
56
+ return date.getDateDiff(currentDate, props.notification.createdAt, 'minutes') < 10
57
+ })
58
+
59
+ const hasBadge = computed(() => isRecentNotification.value && !markedAsRead.value)
60
+
61
+ const dateLabel = computed(() => {
62
+ return isRecentNotification.value
63
+ ? 'Agora mesmo'
64
+ : dateTime(props.notification.createdAt, 'dd/MM/yyyy HH:mm')
65
+ })
66
+
67
+ const buttonProps = computed(() => {
68
+ const { link } = props.notification
69
+
70
+ const urlFromLink = new URL(link)
71
+
72
+ const isExternalURL = urlFromLink.host !== location.host
73
+
74
+ return {
75
+ icon: 'sym_r_chevron_right',
76
+ color: markedAsRead.value ? 'grey-10' : 'primary',
77
+
78
+ /**
79
+ * Se for uma url externa, diferente da URL que ele esta acessando no momento,
80
+ * é adicionado um "href" porque é um link para outro modulo, caso seja para o mesmo modulo,
81
+ * é adicionado um "to" para não recarregar a pagina.
82
+ */
83
+ ...(isExternalURL ? { href: link } : { to: urlFromLink.pathname })
84
+ }
85
+ })
86
+ </script>
@@ -0,0 +1,141 @@
1
+ <template>
2
+ <qas-drawer v-bind="drawerProps" v-model="model">
3
+ <div class="fixed-position">
4
+ <div class="justify-end row">
5
+ <qas-btn class="q-mb-xl" :disable="isAllNotificationsRead" icon="sym_r_check_circle" label="Marcar todas como lida" @click="markAsRead" />
6
+ </div>
7
+
8
+ <qas-infinite-scroll v-model:list="notifications" v-bind="infiniteScrollProps">
9
+ <qas-list-items :list="notifications" :use-box="false" :use-clickable-item="false" :use-section-actions="false">
10
+ <template #item-section="{ item }">
11
+ <pv-layout-notification-card :notification="item" />
12
+ </template>
13
+ </qas-list-items>
14
+ </qas-infinite-scroll>
15
+ </div>
16
+ </qas-drawer>
17
+ </template>
18
+
19
+ <script setup>
20
+ import PvLayoutNotificationCard from './PvLayoutNotificationCard.vue'
21
+ import QasDrawer from '../../drawer/QasDrawer.vue'
22
+ import QasInfiniteScroll from '../../infinite-scroll/QasInfiniteScroll.vue'
23
+
24
+ import useNotifications, { onNotificationReceived } from '../../../composables/use-notifications'
25
+
26
+ import { promiseHandler } from '../../../helpers'
27
+
28
+ import { computed, ref, inject, onMounted } from 'vue'
29
+
30
+ defineOptions({ name: 'PvLayoutNotificationsDrawer' })
31
+
32
+ const props = defineProps({
33
+ model: {
34
+ type: Boolean
35
+ }
36
+ })
37
+
38
+ const emit = defineEmits(['update:modelValue'])
39
+
40
+ const axios = inject('axios')
41
+
42
+ const { setUnreadNotificationsCount } = useNotifications()
43
+
44
+ const hasMadeFirstFetch = ref(false)
45
+ const isMarkingNotificationsAsRead = ref(false)
46
+
47
+ const notifications = ref([])
48
+
49
+ onMounted(() => {
50
+ const notificationsUtilsChannel = new BroadcastChannel('notifications--utils')
51
+
52
+ notificationsUtilsChannel.onmessage = ({ data }) => {
53
+ if (data.type === 'markAllAsRead') onMarkAsReadSuccess()
54
+ }
55
+ })
56
+
57
+ /**
58
+ * Quando o usuário ainda não abriu a central de notificações, a primeira vez que
59
+ * ele abrir vai obter esses dados via API, mesmo que ele tenha recebido a notificação
60
+ * em real time, após ele ter aberto as notificações pelo menos uma vez, todas
61
+ * notificações recebidas em real time serão incrementadas na central de
62
+ * notificação sem a necessidade de chamar a API e resetar a paginação (feita por scroll).
63
+ */
64
+ onNotificationReceived(notification => {
65
+ if (!hasMadeFirstFetch.value) return
66
+
67
+ notifications.value.unshift(notification)
68
+ })
69
+
70
+ const infiniteScrollProps = {
71
+ limitPerPage: 30,
72
+ // os "165px" são referentes ao cabeçalho.
73
+ maxHeight: 'calc(100vh - 165px)',
74
+ url: 'users/me/notifications',
75
+ onFetchSuccess
76
+ }
77
+
78
+ // computed
79
+ const model = computed({
80
+ get () {
81
+ return props.model
82
+ },
83
+
84
+ set (value) {
85
+ emit('update:modelValue', value)
86
+ }
87
+ })
88
+
89
+ const drawerProps = computed(() => {
90
+ return {
91
+ loading: isMarkingNotificationsAsRead.value,
92
+ maxWidth: '60%',
93
+ title: 'Notificações'
94
+ }
95
+ })
96
+
97
+ /**
98
+ * Se todas notificações estiverem lidas, então desabilitar o botão de "Marcar todas como lida"
99
+ */
100
+ const isAllNotificationsRead = computed(() => !notifications.value.some(notification => !notification?.isRead))
101
+
102
+ // functions
103
+ async function markAsRead () {
104
+ const { data } = await promiseHandler(
105
+ axios.patch('/users/me/notifications', { markAllAsRead: true }),
106
+ {
107
+ useLoading: false,
108
+ errorMessage: 'Falha ao marcar todas notificações como lida. Por favor, tente novamente em alguns minutos.',
109
+ onLoading: isLoading => {
110
+ isMarkingNotificationsAsRead.value = isLoading
111
+ }
112
+ }
113
+ )
114
+
115
+ if (data) {
116
+ const notificationsUtilsChannel = new BroadcastChannel('notifications--utils')
117
+
118
+ notificationsUtilsChannel.postMessage({ type: 'markAllAsRead' })
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Ao marcar todas notificações como lida, é necessário percorrer todo o array
124
+ * de "notifications" e setar a prop "isRead" como "true", para não precisar chamar
125
+ * novamente a API para atualizar estes dados.
126
+ */
127
+ function onMarkAsReadSuccess () {
128
+ notifications.value.forEach(notification => {
129
+ notification.isRead = true
130
+ })
131
+
132
+ /**
133
+ * Zera o contador de notificações, responsável pelo ícone no QasAppUser.
134
+ */
135
+ setUnreadNotificationsCount(0)
136
+ }
137
+
138
+ function onFetchSuccess () {
139
+ hasMadeFirstFetch.value = true
140
+ }
141
+ </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <qas-box class="qas-list-items">
2
+ <component :is="component" class="qas-list-items" :class="classes">
3
3
  <q-list separator>
4
4
  <q-item v-for="(item, index) in props.list" :key="index" v-ripple :clickable="props.useClickableItem" @click="onClick({ item, index }, true)">
5
5
  <slot :index="index" :item="item" name="item">
@@ -15,13 +15,14 @@
15
15
  </slot>
16
16
  </q-item>
17
17
  </q-list>
18
- </qas-box>
18
+ </component>
19
19
  </template>
20
20
 
21
21
  <script setup>
22
- import QasBox from '../box/QasBox.vue'
23
22
  import QasBtn from '../btn/QasBtn.vue'
24
23
 
24
+ import { computed } from 'vue'
25
+
25
26
  defineOptions({ name: 'QasListItems' })
26
27
 
27
28
  const props = defineProps({
@@ -35,6 +36,11 @@ const props = defineProps({
35
36
  type: Array
36
37
  },
37
38
 
39
+ useBox: {
40
+ type: Boolean,
41
+ default: true
42
+ },
43
+
38
44
  useClickableItem: {
39
45
  type: Boolean
40
46
  },
@@ -47,6 +53,10 @@ const props = defineProps({
47
53
 
48
54
  const emit = defineEmits(['click-item'])
49
55
 
56
+ const classes = computed(() => ({ 'qas-list-items--no-click': !props.useClickableItem }))
57
+
58
+ const component = computed(() => props.useBox ? 'qas-box' : 'div')
59
+
50
60
  function onClick ({ item, index }, fromItem) {
51
61
  /**
52
62
  * se o click veio do q-item e "useClickableItem" for "false", ou
@@ -62,8 +72,26 @@ function onClick ({ item, index }, fromItem) {
62
72
 
63
73
  <style lang="scss">
64
74
  .qas-list-items {
65
- .q-list > .q-item {
66
- padding: 24px 16px;
75
+ &--no-click {
76
+ .q-item {
77
+ .q-ripple {
78
+ display: none;
79
+ }
80
+ }
81
+ }
82
+
83
+ .q-list {
84
+ & > .q-item {
85
+ padding: var(--qas-spacing-lg) 0;
86
+ }
87
+
88
+ & > .q-item:last-child {
89
+ padding-bottom: 0;
90
+ }
91
+
92
+ & > .q-item:first-child {
93
+ padding-top: 0;
94
+ }
67
95
  }
68
96
  }
69
97
  </style>
@@ -14,6 +14,11 @@ props:
14
14
  default: []
15
15
  type: Array
16
16
 
17
+ use-box:
18
+ desc: Controla se o vai ser um QasBox ou div.
19
+ type: Boolean
20
+ default: true
21
+
17
22
  use-clickable-item:
18
23
  desc: Controla se o item inteiro é clicável ou somente o button dentro do item.
19
24
  type: Boolean
@@ -10,8 +10,8 @@ defineOptions({ name: 'QasWhatsappLink' })
10
10
 
11
11
  const props = defineProps({
12
12
  callingCode: {
13
- type: String,
14
- default: '55'
13
+ type: [Number, String],
14
+ default: 55
15
15
  },
16
16
 
17
17
  phone: {
@@ -7,7 +7,7 @@ props:
7
7
  calling-code:
8
8
  desc: Propriedade para passar o DDI.
9
9
  default: 55
10
- type: String
10
+ type: [Number, String]
11
11
 
12
12
  phone:
13
13
  desc: Propriedade para passar o telefone.
@@ -3,3 +3,6 @@ export { default as useForm } from './use-form.js'
3
3
  export { default as useHistory } from './use-history.js'
4
4
  export { default as useQueryCache } from './use-query-cache.js'
5
5
  export { default as useScreen } from './use-screen.js'
6
+ export { default as useNotifications } from './use-notifications.js'
7
+
8
+ export * from './use-notifications.js'
@@ -0,0 +1,114 @@
1
+ import asteroidConfig from 'asteroid-config'
2
+ import hasParentByClassName from '../helpers/private/has-parent-by-class-name'
3
+
4
+ import { Notify } from 'quasar'
5
+ import { ref } from 'vue'
6
+
7
+ const callbackFunctions = {
8
+ onNotificationReceived: []
9
+ }
10
+
11
+ export function onNotificationReceived (callbackFn) {
12
+ callbackFunctions.onNotificationReceived.push(callbackFn)
13
+ }
14
+
15
+ const unreadNotificationsCount = ref(0)
16
+
17
+ export default function () {
18
+ const isNotificationsEnabled = asteroidConfig.framework.featureToggle?.useNotifications
19
+
20
+ function triggerNotification (notification) {
21
+ callbackFunctions.onNotificationReceived.forEach(fn => fn((notification)))
22
+ }
23
+
24
+ function setUnreadNotificationsCount (value) {
25
+ unreadNotificationsCount.value = value
26
+ }
27
+
28
+ function incrementUnreadNotificationsCount () {
29
+ unreadNotificationsCount.value++
30
+ }
31
+
32
+ /**
33
+ * @param {{
34
+ * message: string
35
+ * title: string
36
+ * link?: string
37
+ * }} config
38
+ */
39
+ function sendNotify (config = {}) {
40
+ const { link } = config
41
+
42
+ const classes = ['bg-white', 'boot-notification', 'q-py-sm', 'text-grey-8']
43
+
44
+ if (link) classes.push(...['boot-notification--has-link', 'cursor-pointer'])
45
+
46
+ const closeNotify = Notify.create({
47
+ actions: [{
48
+ icon: 'sym_r_close',
49
+ class: 'boot-notification__close-button qas-btn qas-btn--tertiary qas-btn--tertiary-grey-10'
50
+ }],
51
+
52
+ attrs: {
53
+ onClick: event => onNotifyClick({ event, link, closeNotify })
54
+ },
55
+
56
+ classes: classes.join(' '),
57
+ html: true,
58
+ icon: 'sym_r_info',
59
+ iconColor: 'primary',
60
+ message: getHTMLMessage(config),
61
+ multiLine: true,
62
+ position: 'bottom-right',
63
+ timeout: 30000
64
+ })
65
+
66
+ /**
67
+ * Função que é chamada quando o usuário clica na notificação, se a notificação
68
+ * tem link, então ele vai ser redirecionado para o link em uma nova aba, caso
69
+ * não tenha link, nada acontece.
70
+ */
71
+ function onNotifyClick ({ event, link, closeNotify }) {
72
+ if (!link) return
73
+
74
+ if (hasParentByClassName('boot-notification__close-button', event.srcElement)) return
75
+
76
+ window.open(link)
77
+
78
+ closeNotify()
79
+ }
80
+
81
+ /**
82
+ * Função que retorna um HTML para seguir estilos propostos pelo design.
83
+ */
84
+ function getHTMLMessage ({ message, title, icon = 'info' } = {}) {
85
+ return (`
86
+ <div>
87
+ <header class="row items-center">
88
+ <h5 class="text-grey-10 text-h5 boot-notification__title">
89
+ ${title}
90
+ </h5>
91
+ </header>
92
+
93
+ <div class="q-mt-sm text-grey-8 text-body1">
94
+ ${message}
95
+ </div>
96
+ </div>
97
+ `)
98
+ }
99
+ }
100
+
101
+ return {
102
+ // const
103
+ isNotificationsEnabled,
104
+
105
+ // computed
106
+ unreadNotificationsCount,
107
+
108
+ // functions
109
+ incrementUnreadNotificationsCount,
110
+ sendNotify,
111
+ setUnreadNotificationsCount,
112
+ triggerNotification
113
+ }
114
+ }