Browse Source

Maintenance: Desktop view - Align error pages with the agreed design.

Dusan Vuckovic 7 months ago
parent
commit
deece596be

+ 1 - 1
app/frontend/apps/desktop/components/layout/LayoutPage.vue

@@ -56,7 +56,7 @@ const {
       </template>
     </LayoutSidebar>
     <div class="relative">
-      <RouterView />
+      <slot><RouterView /></slot>
     </div>
   </div>
 </template>

+ 49 - 2
app/frontend/apps/desktop/pages/error/views/Error.vue

@@ -1,11 +1,58 @@
 <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
 
 <script setup lang="ts">
+import { storeToRefs } from 'pinia'
+import { computed } from 'vue'
+
+import { errorOptions } from '#shared/router/error.ts'
+import { useAuthenticationStore } from '#shared/stores/authentication.ts'
+import { ErrorStatusCodes } from '#shared/types/error.ts'
+
 import LayoutMain from '#desktop/components/layout/LayoutMain.vue'
+import LayoutPage from '#desktop/components/layout/LayoutPage.vue'
+
+const { authenticated } = storeToRefs(useAuthenticationStore())
 
-// TODO: Implement error pages for authenticated & non-authenticated users.
+const errorImage = computed(() => {
+  switch (errorOptions.value.statusCode) {
+    case ErrorStatusCodes.Forbidden:
+      return '/assets/error/error-403.svg'
+    case ErrorStatusCodes.NotFound:
+      return '/assets/error/error-404.svg'
+    case ErrorStatusCodes.InternalError:
+    default:
+      return '/assets/error/error-500.svg'
+  }
+})
 </script>
 
 <template>
-  <LayoutMain class="flex grow flex-col gap-3">ERROR XYZ</LayoutMain>
+  <component
+    :is="authenticated ? LayoutPage : 'div'"
+    :class="{ 'h-full': !authenticated }"
+  >
+    <LayoutMain
+      class="flex grow flex-col items-center justify-center gap-4 bg-blue-50 dark:bg-gray-800"
+    >
+      <img width="540" :alt="$t('Error')" :src="errorImage" />
+      <h1 class="text-center text-xl leading-snug text-black dark:text-white">
+        {{ $t(errorOptions.title) }}
+      </h1>
+      <CommonLabel class="mx-auto max-w-prose text-center" tag="p">
+        {{
+          $t(errorOptions.message, ...(errorOptions.messagePlaceholder || []))
+        }}
+      </CommonLabel>
+      <CommonLabel
+        v-if="errorOptions.route"
+        class="mx-auto max-w-prose text-center"
+        tag="p"
+      >
+        {{ errorOptions.route }}
+      </CommonLabel>
+      <CommonLink v-if="!authenticated" link="/login" size="medium">
+        {{ $t('Please proceed to login') }}
+      </CommonLink>
+    </LayoutMain>
+  </component>
 </template>

+ 5 - 5
app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingPassword.vue

@@ -1,6 +1,8 @@
 <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
 
 <script setup lang="ts">
+import { useRouter } from 'vue-router'
+
 import {
   NotificationTypes,
   useNotifications,
@@ -8,6 +10,7 @@ import {
 import Form from '#shared/components/Form/Form.vue'
 import type { FormSubmitData } from '#shared/components/Form/types.ts'
 import { useForm } from '#shared/components/Form/useForm.ts'
+import { redirectToError } from '#shared/router/error.ts'
 import { MutationHandler } from '#shared/server/apollo/handler/index.ts'
 
 import CommonButton from '#desktop/components/CommonButton/CommonButton.vue'
@@ -23,12 +26,9 @@ defineOptions({
   beforeRouteEnter() {
     const { canChangePassword } = useCheckChangePassword()
 
-    if (!canChangePassword.value) {
-      // TODO: Redirect to error page using redirectToError or something similar.
-      return '/error'
-    }
+    if (canChangePassword.value) return true
 
-    return true
+    return redirectToError(useRouter())
   },
 })
 

+ 4 - 5
app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue

@@ -2,6 +2,7 @@
 
 <script setup lang="ts">
 import { computed } from 'vue'
+import { useRouter } from 'vue-router'
 
 import { NotificationTypes } from '#shared/components/CommonNotifications/types.ts'
 import { useNotifications } from '#shared/components/CommonNotifications/useNotifications.ts'
@@ -15,6 +16,7 @@ import type {
   UserCurrentAccessTokenListQuery,
 } from '#shared/graphql/types.ts'
 import { i18n } from '#shared/i18n/index.ts'
+import { redirectToError } from '#shared/router/error.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'
@@ -38,12 +40,9 @@ defineOptions({
   beforeRouteEnter() {
     const { canUseAccessToken } = useCheckTokenAccess()
 
-    if (!canUseAccessToken.value) {
-      // TODO: Redirect to error page using redirectToError or something similar.
-      return '/error'
-    }
+    if (canUseAccessToken.value) return true
 
-    return true
+    redirectToError(useRouter())
   },
 })
 

+ 3 - 2
app/frontend/apps/desktop/pages/ticket/__tests__/ticket-create.spec.ts

@@ -393,8 +393,9 @@ describe('ticket create view', async () => {
 
       it('redirects to error page', async () => {
         const view = await visitView('/ticket/create')
-        // :TODO adapt as soon as we have real error page
-        expect(view.getByText('ERROR XYZ')).toBeInTheDocument()
+
+        expect(view.getByText('Not Found')).toBeInTheDocument()
+        expect(view.getByText("This page doesn't exist.")).toBeInTheDocument()
       })
     })
 

+ 3 - 3
app/frontend/apps/mobile/pages/error/views/Error.vue

@@ -11,12 +11,12 @@ import CommonBackButton from '#mobile/components/CommonBackButton/CommonBackButt
 const errorImage = computed(() => {
   switch (errorOptions.value.statusCode) {
     case ErrorStatusCodes.Forbidden:
-      return '/assets/error/error-mobile-403.svg'
+      return '/assets/error/error-403.svg'
     case ErrorStatusCodes.NotFound:
-      return '/assets/error/error-mobile-404.svg'
+      return '/assets/error/error-404.svg'
     case ErrorStatusCodes.InternalError:
     default:
-      return '/assets/error/error-mobile-500.svg'
+      return '/assets/error/error-500.svg'
   }
 })
 </script>

+ 2 - 2
app/frontend/apps/mobile/pages/organization/__tests__/organization.spec.ts

@@ -53,7 +53,7 @@ describe('static organization', () => {
 
     await waitUntil(() => mockApi.calls.resolve)
 
-    expect(view.getByText(organization.name || 'not found')).toBeInTheDocument()
+    expect(view.getByText(organization.name || 'Not Found')).toBeInTheDocument()
 
     expect(
       view.getByLabelText(`Avatar (${organization.name})`),
@@ -257,7 +257,7 @@ describe('static organization', () => {
 
     await waitUntil(() => mockApi.calls.error)
 
-    await expect(view.findByText('Not found')).resolves.toBeInTheDocument()
+    await expect(view.findByText('Not Found')).resolves.toBeInTheDocument()
   })
 
   it('redirects to error page if access to organization is forbidden', async () => {

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

@@ -220,7 +220,7 @@ describe('visiting user page', () => {
 
     await waitUntil(() => mockApi.calls.error)
 
-    await expect(view.findByText('Not found')).resolves.toBeInTheDocument()
+    await expect(view.findByText('Not Found')).resolves.toBeInTheDocument()
   })
 
   it('redirects to error page if access to organization is forbidden', async () => {

+ 1 - 1
app/frontend/shared/errors/useErrorHandler.ts

@@ -22,7 +22,7 @@ export const useErrorHandler = () => {
       let statusCode: number
 
       if (errorHandler.type === GraphQLErrorTypes.RecordNotFound) {
-        title = __('Not found')
+        title = __('Not Found')
         message = messages.notFound
         statusCode = ErrorStatusCodes.NotFound
       } else if (errorHandler.type === GraphQLErrorTypes.Forbidden) {

+ 20 - 15
i18n/zammad.pot

@@ -3412,7 +3412,7 @@ msgstr ""
 #: app/assets/javascripts/app/views/profile/token_access.jst.eco:18
 #: app/assets/javascripts/app/views/ssl_certificates_list.jst.eco:14
 #: app/assets/javascripts/app/views/widget/ticket_stats_frequency.jst.eco:20
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:100
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:99
 #: app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/TicketDetailTopBar/TicketInformation.vue:104
 #: app/frontend/apps/mobile/pages/ticket/components/TicketDetailView/ArticleMetadataDialog.vue:124
 #: app/models/report.rb:22
@@ -4606,7 +4606,7 @@ msgstr ""
 msgid "Delete table"
 msgstr ""
 
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:155
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:154
 msgid "Delete this access token"
 msgstr ""
 
@@ -5680,6 +5680,7 @@ msgstr ""
 #: app/assets/javascripts/app/controllers/_settings/area_proxy.coffee:51
 #: app/assets/javascripts/app/controllers/ticket_zoom.coffee:119
 #: app/assets/javascripts/app/controllers/widget/error_modal.coffee:6
+#: app/frontend/apps/desktop/pages/error/views/Error.vue:37
 #: app/frontend/apps/mobile/pages/error/views/Error.vue:39
 msgid "Error"
 msgstr ""
@@ -5846,7 +5847,7 @@ msgstr ""
 #: app/assets/javascripts/app/views/profile/token_access.jst.eco:19
 #: app/assets/javascripts/app/views/profile/token_access_create.jst.eco:10
 #: app/assets/javascripts/app/views/ssl_certificates_list.jst.eco:15
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:105
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:104
 msgid "Expires"
 msgstr ""
 
@@ -7835,7 +7836,7 @@ msgstr ""
 msgid "Last Internal Article"
 msgstr ""
 
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:110
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:109
 msgid "Last Used"
 msgstr ""
 
@@ -9010,7 +9011,7 @@ msgstr ""
 #: app/frontend/apps/desktop/components/TwoFactor/TwoFactorConfiguration/TwoFactorConfigurationSecurityKeys.vue:100
 #: app/frontend/apps/desktop/pages/personal-setting/components/PersonalSettingNewAccessTokenFlyout.vue:31
 #: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingDevices.vue:103
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:90
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:89
 #: app/frontend/apps/desktop/pages/ticket/components/TicketSidebar/TicketSidebarSharedDraftFlyout.vue:142
 #: app/frontend/apps/desktop/pages/ticket/components/TicketSidebar/TicketSidebarSharedDraftStartContent.vue:181
 #: db/seeds/object_manager_attributes.rb:1507
@@ -9148,7 +9149,7 @@ msgid "New Overview"
 msgstr ""
 
 #: app/frontend/apps/desktop/pages/personal-setting/components/PersonalSettingNewAccessTokenFlyout.vue:132
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:202
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:201
 msgid "New Personal Access Token"
 msgstr ""
 
@@ -9566,6 +9567,7 @@ msgstr ""
 #: app/assets/javascripts/app/controllers/_profile/out_of_office.coffee:160
 #: app/assets/javascripts/app/controllers/knowledge_base/agent_controller.coffee:240
 #: app/assets/javascripts/app/controllers/ticket_zoom.coffee:114
+#: app/frontend/shared/errors/useErrorHandler.ts:25
 #: app/frontend/shared/router/error.ts:18
 #: app/helpers/knowledge_base_public_page_title_helper.rb:25
 msgid "Not Found"
@@ -9608,7 +9610,6 @@ msgid "Not configured"
 msgstr ""
 
 #: app/frontend/apps/desktop/components/UserTaskbarTabs/UserTaskbarTabNotFound.vue:18
-#: app/frontend/shared/errors/useErrorHandler.ts:25
 msgid "Not found"
 msgstr ""
 
@@ -10473,12 +10474,12 @@ msgstr ""
 #: app/assets/javascripts/app/models/role.coffee:7
 #: app/assets/javascripts/app/views/object_manager/screens.jst.eco:5
 #: app/assets/javascripts/app/views/profile/token_access.jst.eco:17
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:95
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:94
 msgid "Permissions"
 msgstr ""
 
 #: app/assets/javascripts/app/views/profile/token_access.jst.eco:12
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:213
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:212
 msgid "Personal Access Tokens"
 msgstr ""
 
@@ -10486,7 +10487,7 @@ msgstr ""
 msgid "Personal Settings"
 msgstr ""
 
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:141
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:140
 msgid "Personal access token has been deleted."
 msgstr ""
 
@@ -10524,7 +10525,7 @@ msgid "Phone number is already in use by another WhatsApp account."
 msgstr ""
 
 #: app/assets/javascripts/app/views/profile/token_access.jst.eco:7
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:183
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:182
 msgid "Pick a name for the application, and we'll give you a unique token."
 msgstr ""
 
@@ -10628,6 +10629,10 @@ msgstr ""
 msgid "Please note that this attribute is deprecated within one of the next versions of Zammad. Use the IDP certificate instead."
 msgstr ""
 
+#: app/frontend/apps/desktop/pages/error/views/Error.vue:54
+msgid "Please proceed to login"
+msgstr ""
+
 #: app/assets/javascripts/app/controllers/ticket_zoom/article_action/email_reply.coffee:383
 msgid "Please provide a recipient in \"TO\" or \"CC\"."
 msgstr ""
@@ -13922,7 +13927,7 @@ msgstr ""
 msgid "The password reset request was successful."
 msgstr ""
 
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:132
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:131
 msgid "The personal access token could not be deleted."
 msgstr ""
 
@@ -14591,7 +14596,7 @@ msgid "This class gets added to the button on initialization and will be removed
 msgstr ""
 
 #: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingDevices.vue:170
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:222
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:221
 msgid "This device"
 msgstr ""
 
@@ -15502,7 +15507,7 @@ msgstr ""
 #: app/assets/javascripts/app/views/api.jst.eco:12
 #: app/assets/javascripts/app/views/profile/token_access.jst.eco:2
 #: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSetting/plugins/tokenAccess.ts:8
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:52
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:51
 #: db/seeds/permissions.rb:429
 msgid "Token Access"
 msgstr ""
@@ -16865,7 +16870,7 @@ msgid "You can find a tutorial on how to manage an %s in our online documentatio
 msgstr ""
 
 #: app/assets/javascripts/app/views/profile/token_access.jst.eco:4
-#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:181
+#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingTokenAccess.vue:180
 msgid "You can generate a personal access token for each application you use that needs access to the Zammad API."
 msgstr ""
 

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