Notification.vue 8.2 KB

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