Просмотр исходного кода

Maintenance: Mobile - Verify login page

Vladimir Sheremet 2 лет назад
Родитель
Сommit
b9852c927c

+ 3 - 1
app/frontend/apps/mobile/form/theme/global/getCoreClasses.ts

@@ -7,9 +7,10 @@ type Classes = Record<string, string>
 export const addFloatingLabel = (classes: Classes = {}): Classes => {
   const inputClass = classes.input || ''
   const labelClass = classes.label || ''
+
   return {
     outer: `${classes.outer || ''} floating-input`,
-    wrapper: `${classes.wrapper || ''} relative px-3`,
+    wrapper: `${classes.wrapper || ''} formkit-invalid:bg-red/10 relative px-3`,
     inner: 'flex',
     input: `
       ${inputClass}
@@ -34,6 +35,7 @@ export const addFloatingLabel = (classes: Classes = {}): Classes => {
       formkit-populated:-translate-y-3 formkit-populated:translate-x-6
       formkit-populated:scale-75 formkit-populated:opacity-75
       formkit-required:required
+      formkit-invalid:text-red
     `,
   }
 }

+ 8 - 1
app/frontend/apps/mobile/main.ts

@@ -1,6 +1,6 @@
 // Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
 
-import { createApp } from 'vue'
+import { createApp, unref } from 'vue'
 import '@shared/initializer/translatableMarker'
 import App from '@mobile/App.vue'
 import useSessionStore from '@shared/stores/session'
@@ -16,6 +16,7 @@ import useLocaleStore from '@shared/stores/locale'
 import useAuthenticationStore from '@shared/stores/authentication'
 import 'virtual:svg-icons-register' // eslint-disable-line import/no-unresolved
 import initializeForm from '@mobile/form'
+import { storeToRefs } from 'pinia'
 
 export default async function mountApp(): Promise<void> {
   const app = createApp(App)
@@ -36,6 +37,7 @@ export default async function mountApp(): Promise<void> {
   await session.checkSession()
 
   const application = useApplicationStore()
+  const { config } = storeToRefs(application)
 
   const initalizeAfterSessionCheck: Array<Promise<unknown>> = [
     application.getConfig(),
@@ -54,6 +56,11 @@ export default async function mountApp(): Promise<void> {
   }
 
   app.config.globalProperties.i18n = i18n
+  app.config.globalProperties.$t = i18n.t.bind(i18n)
+  Object.defineProperty(app.config.globalProperties, '$c', {
+    enumerable: true,
+    get: () => unref(config),
+  })
 
   initializeForm(app)
 

+ 2 - 2
app/frontend/apps/mobile/modules/home/views/Home.vue

@@ -60,8 +60,8 @@ const logoutMenu: MenuItem[] = [
 const testingMenu: MenuItem[] = [
   {
     type: 'link',
-    link: '/test',
-    title: 'Testing',
+    link: '/playground',
+    title: 'Playground',
   },
 ]
 </script>

+ 73 - 23
app/frontend/apps/mobile/modules/login/views/Login.vue

@@ -8,12 +8,11 @@ import {
 } from '@shared/components/CommonNotifications'
 import useAuthenticationStore from '@shared/stores/authentication'
 import CommonLogo from '@shared/components/CommonLogo/CommonLogo.vue'
-import useApplicationStore from '@shared/stores/application'
-import { i18n } from '@shared/i18n'
 import Form from '@shared/components/Form/Form.vue'
 import { FormData } from '@shared/components/Form'
-import { FormSchemaId } from '@shared/graphql/types'
 import UserError from '@shared/errors/UserError'
+import { defineFormSchema } from '@mobile/form/composable'
+import useApplicationLoadedStore from '@shared/stores/application'
 
 interface Props {
   invalidatedSession?: string
@@ -21,8 +20,8 @@ interface Props {
 
 const props = defineProps<Props>()
 
-// Output a hint when the session is longer valid.
-// This could happen because because the session was deleted on the server.
+// Output a hint when the session is no longer valid.
+// This could happen because the session was deleted on the server.
 if (props.invalidatedSession === '1') {
   const { notify } = useNotifications()
 
@@ -36,6 +35,55 @@ const authentication = useAuthenticationStore()
 
 const router = useRouter()
 
+const application = useApplicationLoadedStore()
+
+const loginScheme = defineFormSchema([
+  {
+    name: 'login',
+    type: 'text',
+    label: __('Username / Email'),
+    placeholder: __('Username / Email'),
+    required: true,
+    wrapperClass: 'mb-4 rounded-xl bg-gray-500',
+  },
+  {
+    name: 'password',
+    label: __('Password'),
+    placeholder: __('Password'),
+    type: 'password',
+    required: true,
+    wrapperClass: 'mb-4 rounded-xl bg-gray-500',
+  },
+  {
+    isLayout: true,
+    element: 'div',
+    attrs: {
+      class: 'mt-2.5 flex grow items-center justify-between text-white',
+    },
+    children: [
+      {
+        type: 'checkbox',
+        name: 'remember_me',
+        label: __('Remember me'),
+      },
+      // TODO support if/then in form-schema
+      ...(application.config.user_lost_password
+        ? [
+            {
+              isLayout: true,
+              component: 'CommonLink',
+              props: {
+                class: 'text-right text-white',
+                link: '/#password_reset',
+              },
+              children: __('Forgot password?'),
+            },
+          ]
+        : []),
+    ],
+  },
+])
+
 interface LoginFormData {
   login?: string
   password?: string
@@ -56,12 +104,10 @@ const login = (formData: FormData<LoginFormData>) => {
       })
     })
 }
-
-const application = useApplicationStore()
 </script>
 
 <template>
-  <!-- TODO: Only a "first" dummy implementation for the login... -->
+  <!-- TODO: Only a "second" dummy implementation for the login... -->
   <div class="flex h-full min-h-screen flex-col items-center px-7 pt-7 pb-4">
     <div class="m-auto w-full max-w-md">
       <div class="flex grow flex-col justify-center">
@@ -70,47 +116,51 @@ const application = useApplicationStore()
             <CommonLogo />
           </div>
           <div class="mb-6 flex justify-center p-2 text-2xl font-extrabold">
-            {{ application.config.product_name }}
+            {{ $c.product_name }}
           </div>
-          <template v-if="application.config.maintenance_mode">
+          <template v-if="$c.maintenance_mode">
             <div
               class="my-1 flex items-center rounded-xl bg-red py-2 px-4 text-white"
             >
               {{
-                i18n.t(
+                $t(
                   'Zammad is currently in maintenance mode. Only administrators can log in. Please wait until the maintenance window is over.',
                 )
               }}
             </div>
           </template>
-          <template v-if="application.config.maintenance_login">
+          <template v-if="$c.maintenance_login && $c.maintenance_login_message">
             <!-- eslint-disable vue/no-v-html -->
             <div
               class="my-1 flex items-center rounded-xl bg-green py-2 px-4 text-white"
-              v-html="application.config.maintenance_login_message"
+              v-html="$c.maintenance_login_message"
             ></div>
           </template>
           <Form
             ref="form"
             class="text-left"
-            :form-schema-id="FormSchemaId.FormSchemaFormMobileLogin"
+            :schema="loginScheme"
             @submit="login"
           >
             <template #after-fields>
-              <div class="mt-4 flex grow items-center justify-center">
-                <span class="ltr:mr-1 rtl:ml-1">{{ i18n.t('New user?') }}</span>
+              <div
+                v-if="$c.user_create_account"
+                class="mt-4 flex grow items-center justify-center"
+              >
+                <span class="ltr:mr-1 rtl:ml-1">{{ $t('New user?') }}</span>
                 <CommonLink
-                  :link="'TODO'"
+                  link="/#signup"
                   class="cursor-pointer select-none !text-yellow underline"
-                  >{{ i18n.t('Register') }}</CommonLink
                 >
+                  {{ $t('Register') }}
+                </CommonLink>
               </div>
               <FormKit
-                wrapper-class="mx-8 mt-8 flex grow justify-center items-center"
+                wrapper-class="mt-4 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"
                 type="submit"
               >
-                {{ i18n.t('Sign in') }}
+                {{ $t('Sign in') }}
               </FormKit>
             </template>
           </Form>
@@ -118,8 +168,8 @@ const application = useApplicationStore()
       </div>
     </div>
     <div class="mb-6 flex items-center justify-center">
-      <CommonLink link="TODO" class="!text-gray underline">
-        {{ i18n.t('Continue to desktop app') }}
+      <CommonLink link="/#login" class="!text-gray underline">
+        {{ $t('Continue to desktop app') }}
       </CommonLink>
     </div>
     <div class="flex items-center justify-center align-middle text-gray-200">
@@ -131,7 +181,7 @@ const application = useApplicationStore()
       >
         <CommonIcon name="logo" :fixed-size="{ width: 24, height: 24 }" />
       </CommonLink>
-      <span class="ltr:mr-1 rtl:ml-1">{{ i18n.t('Powered by') }}</span>
+      <span class="ltr:mr-1 rtl:ml-1">{{ $t('Powered by') }}</span>
       <CommonLink
         link="https://zammad.org"
         is-external

+ 4 - 4
app/frontend/apps/mobile/modules/test/routes.ts → app/frontend/apps/mobile/modules/playground/routes.ts

@@ -5,12 +5,12 @@ import type { RouteRecordRaw } from 'vue-router'
 
 const routes: RouteRecordRaw[] = [
   {
-    path: '/test',
-    name: 'TestOverview',
+    path: '/playground',
+    name: 'PlaygroundOverview',
     props: true,
-    component: () => import('./views/TestOverview.vue'),
+    component: () => import('./views/PlaygroundOverview.vue'),
     meta: {
-      title: 'Home',
+      title: 'Playground',
       requiresAuth: true,
       requiredPermission: ['*'],
       hasBottomNavigation: true,

+ 10 - 9
app/frontend/apps/mobile/modules/test/views/TestOverview.vue → app/frontend/apps/mobile/modules/playground/views/PlaygroundOverview.vue

@@ -39,15 +39,21 @@ const linkSchemas = defineFormSchema(linkSchemaRaw)
 const schema = defineFormSchema([
   {
     isLayout: true,
-    component: 'FormGroup',
+    component: 'FormLayout',
     props: {
       columns: 2,
     },
     children: [
       {
-        type: 'text',
-        name: 'text22',
-        label: 'Some_Label',
+        isLayout: true,
+        component: 'FormGroup',
+        children: [
+          {
+            type: 'text',
+            name: 'text22',
+            label: 'Some_Label',
+          },
+        ],
       },
       {
         type: 'text',
@@ -56,11 +62,6 @@ const schema = defineFormSchema([
       },
     ],
   },
-  {
-    isLayout: true,
-    component: 'FormGroup',
-    children: linkSchemaRaw,
-  },
 ])
 </script>
 

+ 2 - 0
app/frontend/shared/components/CommonLink/CommonLink.vue

@@ -57,6 +57,8 @@ const { href, route, navigate, isActive, isExactActive } = useLink({
 const isInternalLink = computed(() => {
   if (props.isExternal) return false
   if (props.isRoute) return true
+  // zammad desktop urls
+  if (route.value.fullPath.startsWith('/#')) return false
   return route.value.matched.length > 0 && route.value.name !== 'Error'
 })
 

+ 1 - 0
app/frontend/shared/components/Form/index.ts

@@ -5,6 +5,7 @@ export {
   FormSchemaNode,
   FormValues,
   FormData,
+  AllowedClasses,
 } from './types'
 
 export { default as useForm } from './composable'

+ 2 - 3
app/frontend/shared/components/Form/types.ts

@@ -26,9 +26,7 @@ export enum FormValidationVisibility {
   'submit' = 'submit',
 }
 
-type AllowedClasses =
-  | Record<string, string | Record<string, boolean> | FormKitClasses>
-  | string
+export type AllowedClasses = string | Record<string, boolean> | FormKitClasses
 
 export interface FormSchemaField {
   show?: boolean
@@ -79,6 +77,7 @@ export interface FormSchemaGroupOrList {
 
 interface FormSchemaLayoutBase {
   isLayout: boolean
+  hidden?: string
 }
 
 export interface FormSchemaComponent extends FormSchemaLayoutBase {

+ 0 - 1
app/frontend/shared/form/theme/global/index.ts

@@ -12,7 +12,6 @@ const classes: FormThemeClasses = {
     help: 'mt-0.5 text-xs',
     messages: 'list-none p-0 mt-1 mb-0',
     message: 'text-red mb-1 text-xs',
-    input: 'formkit-invalid:border-red formkit-invalid:border-solid',
   },
   text: defaultTextInput,
   email: defaultTextInput,

Некоторые файлы не были показаны из-за большого количества измененных файлов