Notification.vue 6.8 KB

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