Browse Source

Feature: Mobile - Login finalization - New design for login page (with public links, etc).

Dusan Vuckovic 2 years ago
parent
commit
0085bd1e76

+ 1 - 1
app/frontend/apps/mobile/pages/account/views/AccountOverview.vue

@@ -191,7 +191,7 @@ const installZammadPWA = () => {
     <div class="mb-4">
       <FormKit
         wrapper-class="mt-4 text-base flex grow justify-center items-center"
-        input-class="py-2 px-4 w-full h-14 text-red-bright formkit-variant-primary:bg-red-dark rounded-xl select-none"
+        input-class="py-2 px-4 w-full h-14 !text-red-bright formkit-variant-primary:bg-red-dark rounded-xl select-none"
         type="submit"
         name="signout"
         @click="logout"

+ 14 - 0
app/frontend/apps/mobile/pages/login/__tests__/login-product-branding.spec.ts

@@ -36,4 +36,18 @@ describe('testing login product branding', () => {
       `/assets/images/${applicationConfig.product_logo}`,
     )
   })
+
+  it('check that expected footer logo is present', async () => {
+    const view = await visitView('/login')
+
+    const logo = view.getByAltText('Logo')
+
+    expect(logo).toBeInTheDocument()
+    expect(logo).toHaveAttribute('src', '/assets/images/icons/logo.svg')
+
+    const link = logo.parentElement
+
+    expect(link).toHaveAttribute('href', 'https://zammad.org')
+    expect(link).toHaveAttribute('target', '_blank')
+  })
 })

+ 46 - 22
app/frontend/apps/mobile/pages/login/__tests__/login-public-links.spec.ts

@@ -11,7 +11,7 @@ import { visitView } from '@tests/support/components/visitView'
 import { waitUntilApisResolved } from '@tests/support/utils'
 
 describe('testing login public links', () => {
-  it('renders a single link to desktop app, when no public links are available', async () => {
+  it('always renders a single link to desktop app', async () => {
     const publicLinkQuery = mockPublicLinks([])
     mockPublicLinksSubscription()
 
@@ -19,12 +19,10 @@ describe('testing login public links', () => {
 
     await waitUntilApisResolved(publicLinkQuery)
 
-    const navigation = view.getByRole('navigation')
-    const links = getAllByRole(navigation, 'link')
+    const link = view.getByText('Continue to desktop app')
 
-    expect(links).toHaveLength(1)
-    expect(links[0]).toHaveAttribute('href', '/#login')
-    expect(links[0]).not.toHaveAttribute('target', '_blank')
+    expect(link).toHaveAttribute('href', '/#login')
+    expect(link).not.toHaveAttribute('target', '_blank')
   })
 
   it('renders all public links correctly', async () => {
@@ -56,17 +54,16 @@ describe('testing login public links', () => {
     const navigation = view.getByRole('navigation')
     const links = getAllByRole(navigation, 'link')
 
-    expect(links).toHaveLength(publicLinks.length + 1)
-    expect(links[0]).toHaveAttribute('href', '/#login')
+    expect(links).toHaveLength(publicLinks.length)
 
-    expect(links[1]).toHaveAttribute('href', publicLinks[0].link)
-    expect(links[1]).toHaveAttribute('title', publicLinks[0].description)
-    expect(links[1]).toHaveTextContent(publicLinks[0].title)
-    expect(links[1]).not.toHaveAttribute('target', '_blank')
+    expect(links[0]).toHaveAttribute('href', publicLinks[0].link)
+    expect(links[0]).toHaveAttribute('title', publicLinks[0].description)
+    expect(links[0]).toHaveTextContent(publicLinks[0].title)
+    expect(links[0]).not.toHaveAttribute('target', '_blank')
 
-    expect(links[2]).toHaveAttribute('href', publicLinks[1].link)
-    expect(links[2]).toHaveTextContent(publicLinks[1].title)
-    expect(links[2]).toHaveAttribute('target', '_blank')
+    expect(links[1]).toHaveAttribute('href', publicLinks[1].link)
+    expect(links[1]).toHaveTextContent(publicLinks[1].title)
+    expect(links[1]).toHaveAttribute('target', '_blank')
   })
 
   it('rerenders links, when subscription is triggered', async () => {
@@ -76,9 +73,7 @@ describe('testing login public links', () => {
     const view = await visitView('/login')
     await waitUntilApisResolved(publicLinkQuery)
 
-    const navigation = view.getByRole('navigation')
-
-    expect(getAllByRole(navigation, 'link')).toHaveLength(1)
+    expect(view.queryByRole('navigation')).not.toBeInTheDocument()
 
     await subcription.next({
       data: {
@@ -98,10 +93,39 @@ describe('testing login public links', () => {
       },
     })
 
-    const newLinks = getAllByRole(navigation, 'link')
+    const navigation = view.getByRole('navigation')
+    const links = getAllByRole(navigation, 'link')
+
+    expect(links).toHaveLength(1)
+    expect(links[0]).toHaveAttribute('href', 'https://localhost/link-1')
+  })
+
+  it('always renders a powered by link', async () => {
+    const publicLinkQuery = mockPublicLinks([])
+    mockPublicLinksSubscription()
+
+    const view = await visitView('/login')
+
+    await waitUntilApisResolved(publicLinkQuery)
+
+    const link = view.getByText('Zammad')
+
+    expect(link).toHaveAttribute('href', 'https://zammad.org')
+    expect(link).toHaveAttribute('target', '_blank')
+
+    const text = link.previousElementSibling
+
+    expect(text).toHaveTextContent('Powered by')
+  })
+
+  it('does not render a footer logo by default', async () => {
+    const publicLinkQuery = mockPublicLinks([])
+    mockPublicLinksSubscription()
+
+    const view = await visitView('/login')
+
+    await waitUntilApisResolved(publicLinkQuery)
 
-    expect(newLinks).toHaveLength(2)
-    expect(newLinks[0]).toHaveAttribute('href', '/#login')
-    expect(newLinks[1]).toHaveAttribute('href', 'https://localhost/link-1')
+    expect(view.queryByAltText('Logo')).not.toBeInTheDocument()
   })
 })

+ 16 - 16
app/frontend/apps/mobile/pages/login/views/Login.vue

@@ -133,15 +133,7 @@ publicLinksQuery.subscribeToMore<
 const links = computed(() => {
   const publicLinks = publicLinksQuery.result()
 
-  return [
-    {
-      id: '-1',
-      link: '/#login',
-      title: __('Continue to desktop app'),
-      newTab: false,
-    },
-    ...(publicLinks.value?.publicLinks || []),
-  ]
+  return publicLinks.value?.publicLinks || []
 })
 
 // TODO: workaround for disabled button state, will be changed in formkit.
@@ -226,7 +218,7 @@ const login = (formData: FormData<LoginFormData>) => {
               </div>
               <FormKit
                 wrapper-class="mt-6 flex grow justify-center items-center"
-                input-class="py-2 px-4 w-full h-14 text-xl font-semibold text-black formkit-variant-primary:bg-yellow rounded-xl select-none"
+                input-class="py-2 px-4 w-full h-14 text-xl font-semibold !text-black formkit-variant-primary:bg-yellow rounded-xl select-none"
                 type="submit"
                 :disabled="isDisabled"
               >
@@ -237,21 +229,29 @@ const login = (formData: FormData<LoginFormData>) => {
         </div>
       </div>
     </main>
-    <nav class="mb-6 flex flex-wrap items-center justify-center gap-1">
-      <template v-for="(link, idx) of links" :key="link.id">
+    <CommonLink link="/#login" class="font-medium leading-4 text-gray">
+      {{ $t('Continue to desktop app') }}
+    </CommonLink>
+    <nav
+      v-if="links.length"
+      class="mt-4 flex w-full max-w-md flex-wrap items-center justify-center gap-1"
+    >
+      <template v-for="link in links" :key="link.id">
         <CommonLink
           :link="link.link"
           :title="link.description"
           :open-in-new-tab="link.newTab"
-          class="!text-gray underline"
+          class="font-semibold leading-4 tracking-wide text-gray after:ml-1 after:font-medium after:text-gray-200 after:content-['|'] last:after:content-none"
         >
           {{ $t(link.title) }}
         </CommonLink>
-        <span v-if="idx !== links.length - 1" aria-hidden="true">|</span>
       </template>
     </nav>
-    <footer class="flex items-center justify-center align-middle text-gray">
+    <footer
+      class="mt-8 mb-14 flex w-full max-w-md items-center justify-center border-t border-gray-600 pt-2.5 align-middle font-medium leading-4 text-gray"
+    >
       <CommonLink
+        v-if="application.hasCustomProductBranding"
         link="https://zammad.org"
         external
         open-in-new-tab
@@ -268,7 +268,7 @@ const login = (formData: FormData<LoginFormData>) => {
         link="https://zammad.org"
         external
         open-in-new-tab
-        class="font-semibold !text-gray"
+        class="font-semibold"
       >
         Zammad
       </CommonLink>

+ 1 - 1
app/frontend/apps/mobile/pages/ticket/__tests__/ticket-detail-view.spec.ts

@@ -19,7 +19,7 @@ import {
   mockGraphQLSubscription,
 } from '@tests/support/mock-graphql-api'
 import { mockPermissions } from '@tests/support/mock-permissions'
-import { nullableMock, waitForTimeout, waitUntil } from '@tests/support/utils'
+import { nullableMock, waitUntil } from '@tests/support/utils'
 import { flushPromises } from '@vue/test-utils'
 import { TicketDocument } from '../graphql/queries/ticket.api'
 import { TicketArticlesDocument } from '../graphql/queries/ticket/articles.api'

+ 1 - 1
app/frontend/apps/mobile/pages/ticket/components/TicketDetailView/ArticleReplyDialog.vue

@@ -124,7 +124,7 @@ const close = () => {
       <FormKit
         v-if="newTicketArticlePresent"
         wrapper-class="mt-4 flex grow justify-center items-center"
-        input-class="py-2 px-4 w-full h-14 text-base text-red-bright formkit-variant-primary:bg-red-dark rounded-xl select-none"
+        input-class="py-2 px-4 w-full h-14 text-base !text-red-bright formkit-variant-primary:bg-red-dark rounded-xl select-none"
         type="button"
         name="discardArticle"
         @click="discardDialog"

+ 1 - 1
app/frontend/apps/mobile/pages/ticket/views/TicketCreate.vue

@@ -475,7 +475,7 @@ export default {
       </h1>
       <div class="flex cursor-pointer items-center justify-self-end text-base">
         <FormKit
-          input-class="flex justify-center items-center w-9 h-9 rounded-full text-black text-center formkit-variant-primary:bg-yellow"
+          input-class="flex justify-center items-center w-9 h-9 rounded-full !text-black text-center formkit-variant-primary:bg-yellow"
           type="button"
           :disabled="submitButtonDisabled"
           :title="$t('Create ticket')"

+ 1 - 1
app/frontend/apps/mobile/pages/ticket/views/TicketInformation/TicketInformationDetails.vue

@@ -132,7 +132,7 @@ const { isTicketAgent, isTicketEditable } = useTicketView(ticket)
   <FormKit
     v-if="ticketFormGroupNode?.context?.state.dirty"
     wrapper-class="mt-4 mb-4 flex grow justify-center items-center"
-    input-class="py-2 px-4 w-full h-14 text-base text-red-bright formkit-variant-primary:bg-red-dark rounded-xl select-none"
+    input-class="py-2 px-4 w-full h-14 text-base !text-red-bright formkit-variant-primary:bg-red-dark rounded-xl select-none"
     type="button"
     name="discardTicketInformation"
     @click="discardTicketEditDialog"

+ 2 - 5
app/frontend/shared/components/CommonLogo/CommonLogo.vue

@@ -8,11 +8,8 @@ const assetsPath = '/assets/images'
 const application = useApplicationStore()
 
 const logoUrl = computed(() => {
-  const productLogo = application.config.product_logo as string
-  if (!productLogo) {
-    return `${assetsPath}/logo.svg`
-  }
-  return `${assetsPath}/${productLogo}`
+  if (!application.hasCustomProductBranding) return `${assetsPath}/logo.svg`
+  return `${assetsPath}/${application.config.product_logo}`
 })
 </script>
 

+ 1 - 1
app/frontend/shared/components/Form/fields/FieldTags/__tests__/FieldTags.spec.ts

@@ -12,7 +12,7 @@ import { getByIconName } from '@tests/support/components/iconQueries'
 import type { MockGraphQLInstance } from '@tests/support/mock-graphql-api'
 import { mockGraphQLApi } from '@tests/support/mock-graphql-api'
 import type { FieldTagsProps } from '../types'
-import { waitForTimeout, waitForNextTick, waitUntil } from '../../../../../../tests/support/utils'
+import { waitUntil } from '../../../../../../tests/support/utils'
 
 const defaultTags = [
   { label: 'test', value: 'test' },

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