Notification.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. <template>
  2. <v-btn
  3. ref="btn"
  4. icon
  5. width="48px"
  6. size="small"
  7. >
  8. <v-badge
  9. color="orange"
  10. offset-x="-4"
  11. offset-y="17"
  12. :model-value="unreadNotification.length > 0"
  13. :content="unreadNotification.length">
  14. <v-icon class="text-ot-white">
  15. fa fa-bell
  16. </v-icon>
  17. </v-badge>
  18. </v-btn>
  19. <v-tooltip :activator="btn" location="bottom">
  20. <span>{{ $t('notification') }}</span>
  21. </v-tooltip>
  22. <v-menu
  23. :activator="btn"
  24. v-model="isOpen"
  25. >
  26. <v-card max-width="400">
  27. <v-card-title class="ot-header-menu text-body-2 font-weight-bold">
  28. {{ $t('notification') }}
  29. </v-card-title>
  30. <v-card-text class="ma-0 pa-0 header-menu">
  31. <v-list density="compact" :subheader="true">
  32. <v-list-item
  33. v-for="(notification, index) in notifications"
  34. :key="index"
  35. :class="`${notification.notificationUsers.length === 0 ? 'unread' : ''}`"
  36. >
  37. <v-list-item-content>
  38. <v-list-item-title
  39. class="list_item mt-2 mb-2"
  40. v-text="getMessage(notification)"
  41. style="font-size: 13px; line-height: 16px;"
  42. />
  43. </v-list-item-content>
  44. <template #append>
  45. <v-icon
  46. v-if="notification.link"
  47. icon="mdi:mdi-download"
  48. @click="download(notification.link)"
  49. class="pt-4"
  50. />
  51. </template>
  52. <!-- <v-divider v-if="index < (notifications.length - 1)"/>-->
  53. </v-list-item>
  54. </v-list>
  55. <!--suppress VueUnrecognizedDirective -->
  56. <v-card v-intersect="update"></v-card>
  57. <v-row
  58. v-if="pending"
  59. class="fill-height mt-3 mb-3"
  60. align="center"
  61. justify="center"
  62. >
  63. <v-progress-circular
  64. indeterminate
  65. color="grey lighten-1"
  66. />
  67. </v-row>
  68. </v-card-text>
  69. <v-card-actions class="ma-0 pa-0">
  70. <v-list-item
  71. id="all_notifications"
  72. :key="$t('all_notification')"
  73. :href="notificationUrl"
  74. router
  75. style="width: 100%; height: 52px;"
  76. >
  77. <v-list-item-title
  78. class="text-body-2 text-ot-white"
  79. v-text="$t('all_notification')"
  80. />
  81. </v-list-item>
  82. </v-card-actions>
  83. </v-card>
  84. </v-menu>
  85. </template>
  86. <script setup lang="ts">
  87. import {NOTIFICATION_TYPE, QUERY_TYPE} from "~/types/enum/enums";
  88. import {Notification} from "~/models/Core/Notification";
  89. import {NotificationUsers} from "~/models/Core/NotificationUsers";
  90. import {useAccessProfileStore} from "~/stores/accessProfile";
  91. import {ComputedRef, Ref, ref} from "@vue/reactivity";
  92. import {useEntityFetch} from "~/composables/data/useEntityFetch";
  93. import {Pagination} from "~/types/data";
  94. import {useEntityManager} from "~/composables/data/useEntityManager";
  95. import Url from "~/services/utils/url";
  96. const accessProfileStore = useAccessProfileStore()
  97. const loading: Ref<Boolean> = ref(true)
  98. const isOpen: Ref<Boolean> = ref(false)
  99. const page: Ref<number> = ref(1)
  100. const i18n = useI18n()
  101. const runtimeConfig = useRuntimeConfig()
  102. const btn = ref(null)
  103. const { em } = useEntityManager()
  104. const { fetchCollection } = useEntityFetch()
  105. let { data: collection, pending, refresh } = await fetchCollection(Notification)
  106. /**
  107. * On récupère les Notifications via le store
  108. */
  109. const notifications: ComputedRef = computed(() => {
  110. // TODO: revoir pour reprendre le order by et tout
  111. return collection.value !== null ? collection.value.items : []
  112. })
  113. /**
  114. * Les metadata dépendront de la dernière valeur du GET lancé
  115. */
  116. const pagination: ComputedRef<Pagination> = computed(() => {
  117. return collection.value !== null ? collection.value.pagination : {}
  118. })
  119. /**
  120. * On calcule le nombre de notifications non lues
  121. */
  122. const unreadNotification: ComputedRef<Array<Notification>> = computed(() => {
  123. return notifications.value.filter((notification: Notification) => {
  124. return notification.notificationUsers.length === 0
  125. })
  126. })
  127. /**
  128. * Lorsque l'utilisateur scroll on regarde la nextPage a charger et on le fait que si le pending du fetch est false
  129. * (si on a fini de télécharger les éléments précédents)
  130. */
  131. const update = async () => {
  132. if (
  133. !pending &&
  134. pagination.value &&
  135. pagination.value.next &&
  136. pagination.value.next > 0
  137. ) {
  138. loading.value = true
  139. page.value = pagination.value.next
  140. await refresh()
  141. // Si des notifications n'avaient pas été marquées comme lues, on le fait immédiatement.
  142. markNotificationsAsRead()
  143. }
  144. }
  145. /**
  146. * On construit le message qui va devoir s'afficher pour une notification
  147. * @param notification
  148. */
  149. const getMessage = (notification: Notification) => {
  150. switch (notification.type){
  151. case NOTIFICATION_TYPE.FILE :
  152. return `${i18n.t('your_file')} ${notification.message?.fileName} ${i18n.t('is_ready_to_be_downloaded')}`
  153. case NOTIFICATION_TYPE.MESSAGE:
  154. if (notification.message?.action)
  155. return `${i18n.t('your_message')} ${notification.message?.fileName} ${i18n.t('is_ready_to_be')} ${notification.message.action}`
  156. return `${i18n.t('your_message')} ${notification.message?.about ?? ''} ${i18n.t('has_been_sent')} `
  157. case NOTIFICATION_TYPE.SYSTEM :
  158. if (notification.message?.about)
  159. return `${i18n.t(notification.message.about)}`
  160. break;
  161. default:
  162. return i18n.t(notification.name)
  163. }
  164. }
  165. /**
  166. * Dès l'ouverture du menu, on indique que les notifications non lues, le sont.
  167. */
  168. const unwatch = watch(isOpen, (newValue, oldValue) => {
  169. if (newValue){
  170. markNotificationsAsRead()
  171. }
  172. })
  173. onUnmounted(() => {
  174. unwatch()
  175. })
  176. /**
  177. * Créer une nouvelle notification users coté back.
  178. * @param notification
  179. * @param accessId
  180. */
  181. const createNewNotificationUsers = (notification: Notification, accessId: number) => {
  182. const notificationUsers = em.newInstance(NotificationUsers, {
  183. access:`/api/accesses/${accessId}`,
  184. notification:`/api/notifications/${notification.id}`,
  185. isRead: true
  186. })
  187. em.persist(NotificationUsers, notificationUsers)
  188. notification.notificationUsers = ['read']
  189. }
  190. /**
  191. * Marque les notifications non lues comme lues
  192. */
  193. const markNotificationsAsRead = () => {
  194. unreadNotification.value.map((notification: Notification) => {
  195. if (accessProfileStore.id === null) {
  196. throw new Error('Current access id is null')
  197. }
  198. createNewNotificationUsers(notification, accessProfileStore.id)
  199. })
  200. }
  201. /**
  202. * Download la cible du lien
  203. * @param link
  204. */
  205. const download = (link: string) => {
  206. if (accessProfileStore.id === null) {
  207. throw new Error('Current access id is null')
  208. }
  209. const url_parts: Array<string> = link.split('/api');
  210. if(accessProfileStore.originalAccess)
  211. url_parts[0] = Url.join('api', String(accessProfileStore.originalAccess.id), String(accessProfileStore.id))
  212. else
  213. url_parts[0] = Url.join('api', String(accessProfileStore.id))
  214. window.open(Url.join(runtimeConfig.baseUrlLegacy, url_parts.join('')));
  215. }
  216. const notificationUrl = Url.join(runtimeConfig.baseUrlAdminLegacy, 'notifications/list/')
  217. </script>
  218. <style scoped lang="scss">
  219. #all_notifications{
  220. background: rgb(var(--v-theme-ot-green, white));
  221. color: white;
  222. }
  223. .list_item{
  224. white-space: normal;
  225. }
  226. .unread{
  227. background: #ecf0f5;
  228. }
  229. </style>