Browse Source

Feature: Mobile - Add possibility to subscribe to ticket updates.

Florian Liebe 2 years ago
parent
commit
25eda0e545

+ 12 - 4
app/frontend/apps/mobile/modules/ticket/__tests__/mocks/detailed-view.ts

@@ -15,11 +15,13 @@ const ticketDate = new Date(2022, 0, 29, 0, 0, 0, 0)
 
 export const defaultTicket = mock<TicketQuery>({
   ticket: {
+    __typename: 'Ticket',
     id: '1fs432fdsfsg3qr32d',
     internalId: 1,
     number: '610001',
-    createdAt: ticketDate.toISOString(),
     title: 'Test Ticket View',
+    createdAt: ticketDate.toISOString(),
+    updatedAt: ticketDate.toISOString(),
     owner: {
       firstname: 'Max',
       lastname: 'Mustermann',
@@ -39,6 +41,9 @@ export const defaultTicket = mock<TicketQuery>({
         name: TicketState.Open,
       },
     },
+    group: {
+      name: 'Users',
+    },
     priority: {
       name: 'low',
       uiColor: 'low',
@@ -58,6 +63,7 @@ export const defaultArticles = mock<TicketArticlesQuery>({
     edges: [
       {
         node: {
+          __typename: 'TicketArticle',
           id: '1fs432fdsfsg3qr32d',
           internalId: 1,
           createdAt: ticketDate.toISOString(),
@@ -84,6 +90,7 @@ export const defaultArticles = mock<TicketArticlesQuery>({
       },
       {
         node: {
+          __typename: 'TicketArticle',
           id: '1fs432fdsfsg3qr32f',
           internalId: 2,
           to: address,
@@ -110,6 +117,7 @@ export const defaultArticles = mock<TicketArticlesQuery>({
       },
       {
         node: {
+          __typename: 'TicketArticle',
           id: '1fs432fdsfsg3qr30f',
           internalId: 3,
           to: address,
@@ -142,7 +150,7 @@ export const defaultArticles = mock<TicketArticlesQuery>({
   },
 })
 
-export const mockTicketDetailvViewGql = () => {
+export const mockTicketDetailViewGql = () => {
   mockPermissions(['admin.*'])
 
   const mockApiTicket =
@@ -151,7 +159,7 @@ export const mockTicketDetailvViewGql = () => {
     defaultArticles,
   )
 
-  const waitUntillTickesLoaded = async () => {
+  const waitUntilTicketLoaded = async () => {
     await waitUntil(
       () => mockApiTicket.calls.resolve && mockApiArticles.calls.resolve,
     )
@@ -160,6 +168,6 @@ export const mockTicketDetailvViewGql = () => {
   return {
     mockApiArticles,
     mockApiTicket,
-    waitUntillTickesLoaded,
+    waitUntilTicketLoaded,
   }
 }

+ 3 - 3
app/frontend/apps/mobile/modules/ticket/__tests__/ticket-article-context.spec.ts

@@ -1,7 +1,7 @@
 // Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
 
 import { visitView } from '@tests/support/components/visitView'
-import { mockTicketDetailvViewGql } from './mocks/detailed-view'
+import { mockTicketDetailViewGql } from './mocks/detailed-view'
 
 beforeAll(async () => {
   await import('../components/TicketDetailView/ArticleMetadataDialog.vue')
@@ -9,11 +9,11 @@ beforeAll(async () => {
 
 describe('actions inside article context', () => {
   test('opens metadata', async () => {
-    const { waitUntillTickesLoaded } = mockTicketDetailvViewGql()
+    const { waitUntilTicketLoaded } = mockTicketDetailViewGql()
 
     const view = await visitView('/tickets/1')
 
-    await waitUntillTickesLoaded()
+    await waitUntilTicketLoaded()
 
     const contextTriggers = view.getAllByRole('button', {
       name: 'Article actions',

+ 7 - 7
app/frontend/apps/mobile/modules/ticket/__tests__/ticket-single-view.spec.ts

@@ -11,10 +11,10 @@ import { mockGraphQLApi } from '@tests/support/mock-graphql-api'
 import { waitUntil } from '@tests/support/utils'
 import { flushPromises } from '@vue/test-utils'
 import { TicketDocument } from '../graphql/queries/ticket.api'
-import { mockTicketDetailvViewGql } from './mocks/detailed-view'
+import { mockTicketDetailViewGql } from './mocks/detailed-view'
 
 test('statics inside ticket zoom view', async () => {
-  const { waitUntillTickesLoaded } = mockTicketDetailvViewGql()
+  const { waitUntilTicketLoaded } = mockTicketDetailViewGql()
 
   const view = await visitView('/tickets/1')
 
@@ -22,7 +22,7 @@ test('statics inside ticket zoom view', async () => {
   expect(view.getByTestId('loader-title')).toBeInTheDocument()
   expect(view.getByTestId('loader-header')).toBeInTheDocument()
 
-  await waitUntillTickesLoaded()
+  await waitUntilTicketLoaded()
 
   const header = view.getByTestId('header-content')
 
@@ -66,11 +66,11 @@ test('statics inside ticket zoom view', async () => {
 })
 
 test('can refresh data by pulling up', async () => {
-  const { waitUntillTickesLoaded } = mockTicketDetailvViewGql()
+  const { waitUntilTicketLoaded } = mockTicketDetailViewGql()
 
   const view = await visitView('/tickets/1')
 
-  await waitUntillTickesLoaded()
+  await waitUntilTicketLoaded()
 
   const articlesElement = view.getByRole('group', { name: 'Articles' })
 
@@ -133,11 +133,11 @@ test("redirects to error page, if can't find ticket", async () => {
 })
 
 test('show article context on click', async () => {
-  const { waitUntillTickesLoaded } = mockTicketDetailvViewGql()
+  const { waitUntilTicketLoaded } = mockTicketDetailViewGql()
 
   const view = await visitView('/tickets/1')
 
-  await waitUntillTickesLoaded()
+  await waitUntilTicketLoaded()
 
   vi.useRealTimers()
 

+ 28 - 0
app/frontend/apps/mobile/modules/ticket/graphql/fragments/ticketArticleAttributes.api.ts

@@ -0,0 +1,28 @@
+import * as Types from '../../../../../../shared/graphql/types';
+
+import gql from 'graphql-tag';
+export const TicketArticleAttributesFragmentDoc = gql`
+    fragment ticketArticleAttributes on TicketArticle {
+  id
+  internal
+  body
+  createdAt
+  createdBy {
+    id
+    firstname
+    lastname
+  }
+  sender {
+    name
+  }
+  subject
+  to {
+    raw
+    parsed {
+      name
+      emailAddress
+    }
+  }
+  internal
+}
+    `;

+ 23 - 0
app/frontend/apps/mobile/modules/ticket/graphql/fragments/ticketArticleAttributes.graphql

@@ -0,0 +1,23 @@
+fragment ticketArticleAttributes on TicketArticle {
+  id
+  internal
+  body
+  createdAt
+  createdBy {
+    id
+    firstname
+    lastname
+  }
+  sender {
+    name
+  }
+  subject
+  to {
+    raw
+    parsed {
+      name
+      emailAddress
+    }
+  }
+  internal
+}

+ 40 - 0
app/frontend/apps/mobile/modules/ticket/graphql/fragments/ticketAttributes.api.ts

@@ -0,0 +1,40 @@
+import * as Types from '../../../../../../shared/graphql/types';
+
+import gql from 'graphql-tag';
+export const TicketAttributesFragmentDoc = gql`
+    fragment ticketAttributes on Ticket {
+  id
+  internalId
+  number
+  title
+  createdAt
+  updatedAt
+  owner {
+    firstname
+    lastname
+  }
+  customer {
+    id
+    firstname
+    lastname
+    fullname
+  }
+  organization {
+    name
+  }
+  state {
+    name
+    stateType {
+      name
+    }
+  }
+  group {
+    name
+  }
+  priority {
+    name
+    defaultCreate
+    uiColor
+  }
+}
+    `;

+ 35 - 0
app/frontend/apps/mobile/modules/ticket/graphql/fragments/ticketAttributes.graphql

@@ -0,0 +1,35 @@
+fragment ticketAttributes on Ticket {
+  id
+  internalId
+  number
+  title
+  createdAt
+  updatedAt
+  owner {
+    firstname
+    lastname
+  }
+  customer {
+    id
+    firstname
+    lastname
+    fullname
+  }
+  organization {
+    name
+  }
+  state {
+    name
+    stateType {
+      name
+    }
+  }
+  group {
+    name
+  }
+  priority {
+    name
+    defaultCreate
+    uiColor
+  }
+}

+ 7 - 55
app/frontend/apps/mobile/modules/ticket/graphql/queries/ticket.api.ts

@@ -1,6 +1,8 @@
 import * as Types from '../../../../../../shared/graphql/types';
 
 import gql from 'graphql-tag';
+import { TicketAttributesFragmentDoc } from '../fragments/ticketAttributes.api';
+import { TicketArticleAttributesFragmentDoc } from '../fragments/ticketArticleAttributes.api';
 import { ObjectAttributeValuesFragmentDoc } from '../../../../../../shared/graphql/fragments/objectAttributeValues.api';
 import * as VueApolloComposable from '@vue/apollo-composable';
 import * as VueCompositionApi from 'vue';
@@ -11,63 +13,11 @@ export const TicketDocument = gql`
   ticket(
     ticket: {ticketId: $ticketId, ticketInternalId: $ticketInternalId, ticketNumber: $ticketNumber}
   ) {
-    id
-    internalId
-    number
-    title
-    createdAt
-    updatedAt
-    owner {
-      firstname
-      lastname
-    }
-    customer {
-      id
-      firstname
-      lastname
-      fullname
-    }
-    organization {
-      name
-    }
-    state {
-      name
-      stateType {
-        name
-      }
-    }
-    group {
-      name
-    }
-    priority {
-      name
-      defaultCreate
-      uiColor
-    }
+    ...ticketAttributes
     articles @include(if: $withArticles) {
       edges {
         node {
-          id
-          internal
-          body
-          createdAt
-          createdBy {
-            id
-            firstname
-            lastname
-          }
-          sender {
-            name
-          }
-          subject
-          to {
-            raw
-            parsed {
-              name
-              emailAddress
-            }
-          }
-          internal
+          ...ticketArticleAttributes
         }
       }
     }
@@ -76,7 +26,9 @@ export const TicketDocument = gql`
     }
   }
 }
-    ${ObjectAttributeValuesFragmentDoc}`;
+    ${TicketAttributesFragmentDoc}
+${TicketArticleAttributesFragmentDoc}
+${ObjectAttributeValuesFragmentDoc}`;
 export function useTicketQuery(variables: Types.TicketQueryVariables | VueCompositionApi.Ref<Types.TicketQueryVariables> | ReactiveFunction<Types.TicketQueryVariables> = {}, options: VueApolloComposable.UseQueryOptions<Types.TicketQuery, Types.TicketQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<Types.TicketQuery, Types.TicketQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<Types.TicketQuery, Types.TicketQueryVariables>> = {}) {
   return VueApolloComposable.useQuery<Types.TicketQuery, Types.TicketQueryVariables>(TicketDocument, variables, options);
 }

+ 2 - 54
app/frontend/apps/mobile/modules/ticket/graphql/queries/ticket.graphql

@@ -12,63 +12,11 @@ query ticket(
       ticketNumber: $ticketNumber
     }
   ) {
-    id
-    internalId
-    number
-    title
-    createdAt
-    updatedAt
-    owner {
-      firstname
-      lastname
-    }
-    customer {
-      id
-      firstname
-      lastname
-      fullname
-    }
-    organization {
-      name
-    }
-    state {
-      name
-      stateType {
-        name
-      }
-    }
-    group {
-      name
-    }
-    priority {
-      name
-      defaultCreate
-      uiColor
-    }
+    ...ticketAttributes
     articles @include(if: $withArticles) {
       edges {
         node {
-          id
-          internal
-          body
-          createdAt
-          createdBy {
-            id
-            firstname
-            lastname
-          }
-          sender {
-            name
-          }
-          subject
-          to {
-            raw
-            parsed {
-              name
-              emailAddress
-            }
-          }
-          internal
+          ...ticketArticleAttributes
         }
       }
     }

+ 35 - 0
app/frontend/apps/mobile/modules/ticket/graphql/subscriptions/ticketUpdates.api.ts

@@ -0,0 +1,35 @@
+import * as Types from '../../../../../../shared/graphql/types';
+
+import gql from 'graphql-tag';
+import { TicketAttributesFragmentDoc } from '../fragments/ticketAttributes.api';
+import { TicketArticleAttributesFragmentDoc } from '../fragments/ticketArticleAttributes.api';
+import { ObjectAttributeValuesFragmentDoc } from '../../../../../../shared/graphql/fragments/objectAttributeValues.api';
+import * as VueApolloComposable from '@vue/apollo-composable';
+import * as VueCompositionApi from 'vue';
+export type ReactiveFunction<TParam> = () => TParam;
+
+export const TicketUpdatesDocument = gql`
+    subscription ticketUpdates($ticketId: ID!, $withArticles: Boolean = false, $withObjectAttributes: Boolean = false) {
+  ticketUpdates(ticketId: $ticketId) {
+    ticket {
+      ...ticketAttributes
+      articles @include(if: $withArticles) {
+        edges {
+          node {
+            ...ticketArticleAttributes
+          }
+        }
+      }
+      objectAttributeValues @include(if: $withObjectAttributes) {
+        ...objectAttributeValues
+      }
+    }
+  }
+}
+    ${TicketAttributesFragmentDoc}
+${TicketArticleAttributesFragmentDoc}
+${ObjectAttributeValuesFragmentDoc}`;
+export function useTicketUpdatesSubscription(variables: Types.TicketUpdatesSubscriptionVariables | VueCompositionApi.Ref<Types.TicketUpdatesSubscriptionVariables> | ReactiveFunction<Types.TicketUpdatesSubscriptionVariables>, options: VueApolloComposable.UseSubscriptionOptions<Types.TicketUpdatesSubscription, Types.TicketUpdatesSubscriptionVariables> | VueCompositionApi.Ref<VueApolloComposable.UseSubscriptionOptions<Types.TicketUpdatesSubscription, Types.TicketUpdatesSubscriptionVariables>> | ReactiveFunction<VueApolloComposable.UseSubscriptionOptions<Types.TicketUpdatesSubscription, Types.TicketUpdatesSubscriptionVariables>> = {}) {
+  return VueApolloComposable.useSubscription<Types.TicketUpdatesSubscription, Types.TicketUpdatesSubscriptionVariables>(TicketUpdatesDocument, variables, options);
+}
+export type TicketUpdatesSubscriptionCompositionFunctionResult = VueApolloComposable.UseSubscriptionReturn<Types.TicketUpdatesSubscription, Types.TicketUpdatesSubscriptionVariables>;

Some files were not shown because too many files changed in this diff