123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
- <script setup lang="ts">
- import { isEqual } from 'lodash-es'
- import { storeToRefs } from 'pinia'
- import { computed, reactive, ref, watch } from 'vue'
- import {
- NotificationTypes,
- useNotifications,
- } from '#shared/components/CommonNotifications/index.ts'
- import Form from '#shared/components/Form/Form.vue'
- import type {
- FormSchemaNode,
- FormValues,
- } from '#shared/components/Form/types.ts'
- import { useForm } from '#shared/components/Form/useForm.ts'
- import { useMultiStepForm } from '#shared/components/Form/useMultiStepForm.ts'
- import { defineFormSchema } from '#shared/form/defineFormSchema.ts'
- import MutationHandler from '#shared/server/apollo/handler/MutationHandler.ts'
- import QueryHandler from '#shared/server/apollo/handler/QueryHandler.ts'
- import { useSessionStore } from '#shared/stores/session.ts'
- import CommonInputCopyToClipboard from '#desktop/components/CommonInputCopyToClipboard/CommonInputCopyToClipboard.vue'
- import CommonTabManager from '#desktop/components/CommonTabManager/CommonTabManager.vue'
- import LayoutContent from '#desktop/components/layout/LayoutContent.vue'
- import { useBreadcrumb } from '../composables/useBreadcrumb.ts'
- import { useUserCurrentCalendarSubscriptionUpdateMutation } from '../graphql/mutations/userCurrentCalendarSubscriptionUpdate.api.ts'
- import { useUserCurrentCalendarSubscriptionListQuery } from '../graphql/queries/userCurrentCalendarSubscriptionList.api.ts'
- const { breadcrumbItems } = useBreadcrumb(__('Calendar'))
- const { form, isDirty, node, formReset, formSubmit, values } = useForm()
- const { multiStepPlugin, allSteps, activeStep } = useMultiStepForm(node)
- const getFormSchemaGroupSection = (
- stepName: string,
- children: FormSchemaNode[],
- ) => {
- return {
- isLayout: true,
- element: 'section',
- attrs: {
- style: {
- if: `$activeStep !== "${stepName}"`,
- then: 'display: none;',
- },
- },
- children: [
- {
- type: 'group',
- name: stepName,
- isGroupOrList: true,
- plugins: [multiStepPlugin],
- children,
- },
- ],
- }
- }
- const escalationSection = getFormSchemaGroupSection('escalation', [
- {
- name: 'escalationOwn',
- type: 'toggle',
- label: __('My tickets'),
- help: __('Include your own tickets in subscription for escalated tickets.'),
- props: {
- variants: {
- true: 'yes',
- false: 'no',
- },
- },
- },
- {
- name: 'escalationNotAssigned',
- type: 'toggle',
- label: __('Not assigned'),
- help: __(
- 'Include unassigned tickets in subscription for escalated tickets.',
- ),
- props: {
- variants: {
- true: 'yes',
- false: 'no',
- },
- },
- },
- ])
- const newOpenSection = getFormSchemaGroupSection('newOpen', [
- {
- name: 'newOpenOwn',
- type: 'toggle',
- label: __('My tickets'),
- help: __(
- 'Include your own tickets in subscription for new & open tickets.',
- ),
- props: {
- variants: {
- true: 'yes',
- false: 'no',
- },
- },
- },
- {
- name: 'newOpenNotAssigned',
- type: 'toggle',
- label: __('Not assigned'),
- help: __(
- 'Include unassigned tickets in subscription for new & open tickets.',
- ),
- props: {
- variants: {
- true: 'yes',
- false: 'no',
- },
- },
- },
- ])
- const pendingSection = getFormSchemaGroupSection('pending', [
- {
- name: 'pendingOwn',
- type: 'toggle',
- label: __('My tickets'),
- help: __('Include your own tickets in subscription for pending tickets.'),
- props: {
- variants: {
- true: 'yes',
- false: 'no',
- },
- },
- },
- {
- name: 'pendingNotAssigned',
- type: 'toggle',
- label: __('Not assigned'),
- help: __('Include unassigned tickets in subscription for pending tickets.'),
- props: {
- variants: {
- true: 'yes',
- false: 'no',
- },
- },
- },
- ])
- const formSchema = defineFormSchema([
- escalationSection,
- newOpenSection,
- pendingSection,
- ])
- const schemaData = reactive({
- activeStep,
- })
- const calendarSubscriptionListQuery = new QueryHandler(
- useUserCurrentCalendarSubscriptionListQuery(),
- )
- const calendarSubscriptionListQueryResult =
- calendarSubscriptionListQuery.result()
- const { user } = storeToRefs(useSessionStore())
- // Refetch calendar subscription list query when the user preference has changed.
- watch(
- () => user.value?.preferences?.calendar_subscriptions,
- () => {
- calendarSubscriptionListQuery.refetch()
- },
- { deep: true },
- )
- const combinedSubscriptionURL = computed(
- () =>
- calendarSubscriptionListQueryResult.value
- ?.userCurrentCalendarSubscriptionList.combinedUrl ?? '',
- )
- // Alarm is a global option and therefore hoisted out of the multi-step form.
- // Here we keep track of its value from the query, and update it whenever is mutated from outside.
- const alarm = computed(() =>
- Boolean(
- calendarSubscriptionListQueryResult.value
- ?.userCurrentCalendarSubscriptionList.globalOptions?.alarm,
- ),
- )
- const alarmLocalValue = ref(alarm.value)
- watch(alarm, (newValue) => {
- alarmLocalValue.value = newValue
- })
- const directSubscriptionURL = computed(
- () =>
- calendarSubscriptionListQueryResult.value
- ?.userCurrentCalendarSubscriptionList[
- activeStep.value as 'escalation' | 'newOpen' | 'pending'
- ]?.url ?? '',
- )
- const formInitialValues = computed<FormValues>((oldValues) => {
- const values = {
- escalationOwn:
- calendarSubscriptionListQueryResult.value
- ?.userCurrentCalendarSubscriptionList.escalation?.options?.own,
- escalationNotAssigned:
- calendarSubscriptionListQueryResult.value
- ?.userCurrentCalendarSubscriptionList.escalation?.options?.notAssigned,
- newOpenOwn:
- calendarSubscriptionListQueryResult.value
- ?.userCurrentCalendarSubscriptionList.newOpen?.options?.own,
- newOpenNotAssigned:
- calendarSubscriptionListQueryResult.value
- ?.userCurrentCalendarSubscriptionList.newOpen?.options?.notAssigned,
- pendingOwn:
- calendarSubscriptionListQueryResult.value
- ?.userCurrentCalendarSubscriptionList.pending?.options?.own,
- pendingNotAssigned:
- calendarSubscriptionListQueryResult.value
- ?.userCurrentCalendarSubscriptionList.pending?.options?.notAssigned,
- } as unknown as FormValues
- if (oldValues && isEqual(values, oldValues)) return oldValues
- return values
- })
- watch(formInitialValues, (newValues) => {
- // No reset needed when the form has already the correct state.
- if (isEqual(values.value, newValues) && !isDirty.value) return
- formReset({ values: newValues })
- })
- const { notify } = useNotifications()
- const submitForm = async (data: FormValues) => {
- const input = {
- alarm: alarmLocalValue.value,
- escalation: {
- own: Boolean(data.escalationOwn),
- notAssigned: Boolean(data.escalationNotAssigned),
- },
- newOpen: {
- own: Boolean(data.newOpenOwn),
- notAssigned: Boolean(data.newOpenNotAssigned),
- },
- pending: {
- own: Boolean(data.pendingOwn),
- notAssigned: Boolean(data.pendingNotAssigned),
- },
- }
- const calendarSubscriptionUpdateMutation = new MutationHandler(
- useUserCurrentCalendarSubscriptionUpdateMutation(),
- {
- errorNotificationMessage: __(
- 'Updating your calendar subscription settings failed.',
- ),
- },
- )
- return calendarSubscriptionUpdateMutation.send({ input }).then(() => {
- notify({
- id: 'calendar-subscription-update-success',
- type: NotificationTypes.Success,
- message: __('You calendar subscription settings were updated.'),
- })
- })
- }
- const tabs = [
- {
- label: __('Escalated Tickets'),
- key: 'escalation',
- },
- {
- label: __('New & Open Tickets'),
- key: 'newOpen',
- },
- {
- label: __('Pending Tickets'),
- key: 'pending',
- },
- ]
- </script>
- <template>
- <LayoutContent
- :breadcrumb-items="breadcrumbItems"
- :help-text="
- $t(
- 'See your tickets from within your favorite calendar by adding the subscription URL to your calendar app.',
- )
- "
- width="narrow"
- >
- <div class="mb-4">
- <CommonInputCopyToClipboard
- :label="__('Combined subscription URL')"
- :copy-button-text="__('Copy URL')"
- :value="combinedSubscriptionURL"
- :help="__('Includes escalated, new & open and pending tickets.')"
- />
- <FormKit
- v-model="alarmLocalValue"
- type="toggle"
- :label="__('Add alarm to pending reminder and escalated tickets')"
- :variants="{ true: 'yes', false: 'no' }"
- @update:model-value="formSubmit"
- />
- <CommonLabel role="heading" aria-level="2" class="mb-2 mt-5" size="large">
- {{ $t('Subscription settings') }}
- </CommonLabel>
- <CommonTabManager v-model="activeStep" class="mb-3" :tabs="tabs" />
- <div
- :id="`tab-panel-${activeStep}`"
- role="tabpanel"
- :aria-labelledby="`tab-label-${activeStep}`"
- >
- <CommonInputCopyToClipboard
- :label="__('Direct subscription URL')"
- :copy-button-text="__('Copy URL')"
- :value="directSubscriptionURL"
- />
- <Form
- id="calendar-subscription"
- ref="form"
- :schema="formSchema"
- :flatten-form-groups="Object.keys(allSteps)"
- :initial-values="formInitialValues"
- :schema-data="schemaData"
- @changed="formSubmit"
- @submit="submitForm"
- />
- </div>
- </div>
- </LayoutContent>
- </template>
|