PersonalSettingNotifications.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { isEqual } from 'lodash-es'
  4. import { storeToRefs } from 'pinia'
  5. import { computed, ref, watch } from 'vue'
  6. import {
  7. NotificationTypes,
  8. useNotifications,
  9. } from '#shared/components/CommonNotifications/index.ts'
  10. import Form from '#shared/components/Form/Form.vue'
  11. import { type FormSubmitData } from '#shared/components/Form/types.ts'
  12. import { useForm } from '#shared/components/Form/useForm.ts'
  13. import { useConfirmation } from '#shared/composables/useConfirmation.ts'
  14. import { defineFormSchema } from '#shared/form/defineFormSchema.ts'
  15. import {
  16. EnumFormUpdaterId,
  17. EnumNotificationSoundFile,
  18. type UserNotificationMatrixInput,
  19. } from '#shared/graphql/types.ts'
  20. import { convertToGraphQLId } from '#shared/graphql/utils.ts'
  21. import { MutationHandler } from '#shared/server/apollo/handler/index.ts'
  22. import { useSessionStore } from '#shared/stores/session.ts'
  23. import type { UserData } from '#shared/types/store.ts'
  24. import CommonButton from '#desktop/components/CommonButton/CommonButton.vue'
  25. import LayoutContent from '#desktop/components/layout/LayoutContent.vue'
  26. import { useBreadcrumb } from '#desktop/pages/personal-setting/composables/useBreadcrumb.ts'
  27. import { useUserCurrentNotificationPreferencesResetMutation } from '#desktop/pages/personal-setting/graphql/mutations/userCurrentNotificationPreferencesReset.api.ts'
  28. import { useUserCurrentNotificationPreferencesUpdateMutation } from '#desktop/pages/personal-setting/graphql/mutations/userCurrentNotificationPreferencesUpdate.api.ts'
  29. import type { NotificationFormData } from '#desktop/pages/personal-setting/types/notifications.ts'
  30. const { breadcrumbItems } = useBreadcrumb(__('Notifications'))
  31. const { user } = storeToRefs(useSessionStore())
  32. const { notify } = useNotifications()
  33. const { waitForConfirmation } = useConfirmation()
  34. const loading = ref(false)
  35. const { form, onChangedField, formReset, values, isDirty } = useForm()
  36. const soundOptions = Object.keys(EnumNotificationSoundFile).map((sound) => ({
  37. label: sound,
  38. value: sound,
  39. }))
  40. const schema = defineFormSchema([
  41. {
  42. type: 'notifications',
  43. name: 'matrix',
  44. label: __('Notification matrix'),
  45. labelSrOnly: true,
  46. },
  47. {
  48. type: 'select',
  49. name: 'group_ids',
  50. label: __('Limit notifications to specific groups'),
  51. help: __('Affects only notifications for not assigned and all tickets.'),
  52. props: {
  53. clearable: true,
  54. multiple: true,
  55. noOptionsLabelTranslation: true,
  56. },
  57. },
  58. {
  59. type: 'select',
  60. name: 'file',
  61. label: __('Notification sound'),
  62. props: {
  63. options: soundOptions,
  64. },
  65. },
  66. {
  67. type: 'toggle',
  68. name: 'enabled',
  69. label: __('Play user interface sound effects'),
  70. props: {
  71. variants: { true: 'True', false: 'False' },
  72. },
  73. },
  74. ])
  75. const initialFormValues = computed<NotificationFormData>((oldValues) => {
  76. const { notificationConfig = {}, notificationSound = {} } =
  77. user.value?.personalSettings || {}
  78. const values: NotificationFormData = {
  79. group_ids: notificationConfig?.groupIds ?? [],
  80. matrix: notificationConfig?.matrix || {},
  81. // Default notification sound settings are not present on the user preferences.
  82. file: notificationSound?.file ?? EnumNotificationSoundFile.Xylo,
  83. enabled: notificationSound?.enabled ?? true,
  84. }
  85. if (oldValues && isEqual(values, oldValues)) return oldValues
  86. return values
  87. })
  88. watch(initialFormValues, (newValues) => {
  89. // No reset needed when the form has already the correct state.
  90. if (isEqual(values.value, newValues) && !isDirty.value) return
  91. formReset(newValues)
  92. })
  93. onChangedField('file', (fileName) => {
  94. new Audio(`/assets/sounds/${fileName?.toString()}.mp3`)?.play()
  95. })
  96. const onSubmit = async (form: FormSubmitData<NotificationFormData>) => {
  97. loading.value = true
  98. const notificationUpdateMutation = new MutationHandler(
  99. useUserCurrentNotificationPreferencesUpdateMutation(),
  100. {
  101. errorNotificationMessage: __('Notification settings could not be saved.'),
  102. },
  103. )
  104. return notificationUpdateMutation
  105. .send({
  106. matrix: form.matrix as UserNotificationMatrixInput,
  107. groupIds:
  108. form?.group_ids?.map((id) => convertToGraphQLId('Group', id)) || [],
  109. sound: {
  110. file: form.file as EnumNotificationSoundFile,
  111. enabled: form.enabled,
  112. },
  113. })
  114. .then((response) => {
  115. if (response?.userCurrentNotificationPreferencesUpdate) {
  116. notify({
  117. id: 'notification-update-success',
  118. type: NotificationTypes.Success,
  119. message: __('Notification settings have been saved successfully.'),
  120. })
  121. }
  122. })
  123. .finally(() => {
  124. loading.value = false
  125. })
  126. }
  127. const resetFormToDefaults = (
  128. personalSettings: UserData['personalSettings'],
  129. ) => {
  130. form.value?.resetForm({
  131. matrix: personalSettings?.notificationConfig?.matrix || {},
  132. })
  133. }
  134. const onResetToDefaultSettings = async () => {
  135. const confirmed = await waitForConfirmation(
  136. __('Are you sure? Your notifications settings will be reset to default.'),
  137. )
  138. if (!confirmed) return
  139. loading.value = true
  140. const notificationResetMutation = new MutationHandler(
  141. useUserCurrentNotificationPreferencesResetMutation(),
  142. {
  143. errorNotificationMessage: __('Notification settings could not be reset.'),
  144. },
  145. )
  146. return notificationResetMutation
  147. .send()
  148. .then((response) => {
  149. const personalSettings =
  150. response?.userCurrentNotificationPreferencesReset?.user
  151. ?.personalSettings
  152. if (!personalSettings) return
  153. resetFormToDefaults(personalSettings)
  154. notify({
  155. id: 'notification-reset-success',
  156. type: NotificationTypes.Success,
  157. message: __('Notification settings have been reset to default.'),
  158. })
  159. })
  160. .finally(() => {
  161. loading.value = false
  162. })
  163. }
  164. </script>
  165. <template>
  166. <LayoutContent :breadcrumb-items="breadcrumbItems" width="narrow">
  167. <div class="mb-4">
  168. <Form
  169. id="notifications-form"
  170. ref="form"
  171. :schema="schema"
  172. :form-updater-id="EnumFormUpdaterId.FormUpdaterUpdaterUserNotifications"
  173. form-updater-initial-only
  174. :initial-values="initialFormValues"
  175. @submit="onSubmit($event as FormSubmitData<NotificationFormData>)"
  176. >
  177. <template #after-fields>
  178. <div class="flex justify-end gap-2">
  179. <CommonButton
  180. size="medium"
  181. variant="danger"
  182. :disabled="loading"
  183. @click="onResetToDefaultSettings"
  184. >
  185. {{ $t('Reset to Default Settings') }}
  186. </CommonButton>
  187. <CommonButton
  188. size="medium"
  189. type="submit"
  190. variant="submit"
  191. :disabled="loading"
  192. >
  193. {{ $t('Save Notifications') }}
  194. </CommonButton>
  195. </div>
  196. </template>
  197. </Form>
  198. </div>
  199. </LayoutContent>
  200. </template>