Browse Source

Feature: Mobile - Added general layout, style and assets improvements.

Dominik Klein 3 years ago
parent
commit
9f20f4cb96

+ 22 - 1
.eslintrc.js

@@ -1,3 +1,5 @@
+// Copyright (C) 2012-2021 Zammad Foundation, https://zammad-foundation.org/
+
 const path = require('path')
 
 module.exports = {
@@ -31,10 +33,29 @@ module.exports = {
         order: ['template', 'script', 'style'],
       },
     ],
+    // Not allow the usage of relative imports, because we want always use the path aliases.
+    'no-restricted-imports': [
+      'error',
+      {
+        patterns: [
+          {
+            group: ['.*'],
+            message:
+              'Usage of relative imports is not allowed. Always path aliases should be used.',
+          },
+        ],
+      },
+    ],
     // Disable the following rule, because it's not relevant for the tool chain and test envoirment.
     'import/no-extraneous-dependencies': [
       'error',
-      { devDependencies: ['vite.config.ts', 'app/frontend/tests/**/*'] },
+      {
+        devDependencies: [
+          'tailwind.config.js',
+          'vite.config.ts',
+          'app/frontend/tests/**/*',
+        ],
+      },
     ],
     // Adding typescript file types, because airbnb doesn't allow this by default.
     'import/extensions': [

+ 4 - 7
app/frontend/apps/mobile/App.vue

@@ -4,12 +4,9 @@
   <CommonNotifications />
   <div
     class="
-      h-full
-      max-h-full
-      overflow-auto
-      w-full
-      bg-gray-200
-      text-center text-sm
+      min-h-screen min-w-screen
+      bg-dark
+      text-gray-400 text-center text-sm
       antialiased
       font-sans
       select-none
@@ -24,7 +21,7 @@
 </template>
 
 <script setup lang="ts">
-import CommonNotifications from '@common/components/CommonNotifications.vue'
+import CommonNotifications from '@common/components/common/CommonNotifications.vue'
 import useApplicationLoadedStore from '@common/stores/application/loaded'
 import useAuthenticatedStore from '@common/stores/authenticated'
 import useSessionIdStore from '@common/stores/session/id'

+ 16 - 0
app/frontend/apps/mobile/components/layout/LayoutMain.vue

@@ -0,0 +1,16 @@
+<!-- Copyright (C) 2012-2021 Zammad Foundation, https://zammad-foundation.org/ -->
+
+<template>
+  <div class="flex flex-col h-screen overflow-hidden">
+    <main class="flex-1 overflow-y-scroll">
+      <router-view v-slot="{ Component }">
+        <transition>
+          <component v-bind:is="Component" />
+        </transition>
+      </router-view>
+    </main>
+    <footer class="w-full bg-darker text-center border-t p-4">
+      Bottom-Navigation
+    </footer>
+  </div>
+</template>

+ 11 - 0
app/frontend/apps/mobile/main.ts

@@ -15,9 +15,20 @@ import useApplicationConfigStore from '@common//stores/application/config'
 import initializeRouter from '@common/router/index'
 import routes from '@mobile/router'
 
+const enableLoadingAnimation = (): void => {
+  const loadingElement: Maybe<HTMLElement> =
+    document.getElementById('loadingApp')
+
+  if (loadingElement) {
+    loadingElement.style.display = 'flex'
+  }
+}
+
 export default async function mountApp(): Promise<void> {
   const app = createApp(App)
 
+  enableLoadingAnimation()
+
   app.provide(DefaultApolloClient, apolloClient)
 
   provideApolloClient(apolloClient)

+ 16 - 6
app/frontend/apps/mobile/router/index.ts

@@ -3,8 +3,21 @@
 import { RouteRecordRaw } from 'vue-router'
 import Login from '@mobile/views/Login.vue'
 import Home from '@mobile/views/Home.vue'
+import LayoutMain from '@mobile/components/layout/LayoutMain.vue'
 
-// TODO ...extend "meta" in RouteRecordRaw with real type behind.
+// TODO ...extend "meta" in RouteRecordRaw with real type behind if possible.
+
+const mainRoutes: Array<RouteRecordRaw> = [
+  {
+    path: '/',
+    name: 'Home',
+    props: true,
+    component: Home,
+    meta: {
+      requiresAuth: true,
+    },
+  },
+]
 
 const routes: Array<RouteRecordRaw> = [
   {
@@ -16,12 +29,9 @@ const routes: Array<RouteRecordRaw> = [
   },
   {
     path: '/',
-    name: 'Home',
     props: true,
-    component: Home,
-    meta: {
-      requiresAuth: true,
-    },
+    component: LayoutMain,
+    children: mainRoutes,
   },
 ]
 

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

@@ -8,6 +8,14 @@
     <p v-on:click="logout">Logout</p>
     <br />
     <p v-on:click="refetchConfig">refetchConfig</p>
+    <br /><br />
+    <h1 class="text-lg mb-4">Configs:</h1>
+    <template v-if="result && result.applicationConfig">
+      <p v-for="item in result.applicationConfig" v-bind:key="item.key">
+        Key: {{ item.key }}<br />
+        Value: {{ item.value }} <br /><br />
+      </p>
+    </template>
   </div>
 </template>
 
@@ -18,8 +26,10 @@ import useSessionUserStore from '@common/stores/session/user'
 import { storeToRefs } from 'pinia'
 import { useRouter } from 'vue-router'
 import useApplicationConfigStore from '@common/stores/application/config'
+import { QueryHandler } from '@common/server/apollo/handler'
+import { useApplicationConfigQuery } from '@mobile/graphql/api'
 
-// TODO ... only testing the notifications.
+// TODO: Only testing for the notifications...
 const { notify } = useNotifications()
 
 notify({
@@ -41,7 +51,13 @@ const logout = (): void => {
   })
 }
 
+const config = useApplicationConfigStore()
+
 const refetchConfig = async (): Promise<void> => {
-  await useApplicationConfigStore().getConfig(true)
+  await config.getConfig(true)
 }
+
+// TODO: Only testing for the query handler...
+const configQuery = new QueryHandler(useApplicationConfigQuery())
+const result = configQuery.result()
 </script>

+ 114 - 9
app/frontend/apps/mobile/views/Login.vue

@@ -1,19 +1,122 @@
 <!-- Copyright (C) 2012-2021 Zammad Foundation, https://zammad-foundation.org/ -->
 
 <template>
-  <div>
-    <h1>Login</h1>
-    <p>Username: <input v-model="loginFormValues.login" type="text" /></p>
-    <br />
-    <p>
-      Password: <input v-model="loginFormValues.password" type="password" />
-    </p>
-    <br />
-    <button v-on:click="login">Login</button>
+  <!-- TODO: Only a dummy implementation for the login... -->
+  <div class="flex flex-col h-screen items-center justify-center">
+    <div class="max-w-sm w-full">
+      <div class="h-full m-auto">
+        <div class="flex-grow flex flex-col justify-center">
+          <p>Log in with {{ config.get('fqdn') }}</p>
+
+          <div class="my-5 p-5 max-w-full bg-white flex-grow rounded-md">
+            <div class="flex flex-col">
+              <div class="flex justify-center p-4">
+                <img
+                  class="w-32 h-32"
+                  src="@common/assets/logo.svg"
+                  alt="Zammad"
+                />
+              </div>
+
+              <div class="text-left">
+                <label class="block mt-4 cursor-pointer">
+                  <span class="text-gray-600 uppercase text-xs tracking-wide">
+                    Username / Email
+                  </span>
+                  <input
+                    v-model="loginFormValues.login"
+                    type="text"
+                    class="
+                      text-gray-700
+                      mt-1
+                      block
+                      w-full
+                      text-sm
+                      rounded
+                      border border-gray-200
+                    "
+                  />
+                </label>
+
+                <label class="block mt-4 cursor-pointer">
+                  <span class="text-gray-600 uppercase text-xs tracking-wide">
+                    Password
+                  </span>
+                  <input
+                    v-model="loginFormValues.password"
+                    type="password"
+                    class="
+                      text-gray-700
+                      mt-1
+                      block
+                      w-full
+                      text-sm
+                      rounded
+                      border border-gray-200
+                    "
+                  />
+                </label>
+
+                <label
+                  class="
+                    mt-4
+                    cursor-pointer
+                    inline-flex
+                    items-center
+                    select-none
+                  "
+                >
+                  <input type="checkbox" class="form-checkbox" checked />
+                  <span class="ml-2">Remember me</span>
+                </label>
+              </div>
+
+              <div class="flex justify-between flex-grow items-baseline mt-4">
+                <button
+                  class="
+                    bg-blue-500
+                    hover:bg-blue-600
+                    text-white
+                    py-2
+                    px-4
+                    rounded
+                    select-none
+                  "
+                  v-on:click="login"
+                >
+                  Sign in
+                </button>
+
+                <a class="cursor-pointer select-none underline">
+                  Forgot password?
+                </a>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="flex justify-center items-center align-baseline p-6">
+        <a href="https://zammad.org" target="_blank">
+          <svg class="w-10 h-10">
+            <use xlink:href="@common/assets/icons.svg#icon-logo"></use>
+          </svg>
+        </a>
+
+        <span class="mx-1">Powered by</span>
+
+        <a class="ml-1 -mt-1" href="https://zammad.org" target="_blank">
+          <svg class="w-20 h-10 fill-current">
+            <use xlink:href="@common/assets/icons.svg#icon-logotype"></use>
+          </svg>
+        </a>
+      </div>
+    </div>
   </div>
 </template>
 
 <script setup lang="ts">
+import useApplicationConfigStore from '@common/stores/application/config'
 import useAuthenticationStore from '@common/stores/authenticated'
 import { useRouter } from 'vue-router'
 
@@ -32,4 +135,6 @@ const login = (): void => {
       router.replace('/')
     })
 }
+
+const config = useApplicationConfigStore()
 </script>

File diff suppressed because it is too large
+ 289 - 0
app/frontend/common/assets/icons.svg


+ 30 - 0
app/frontend/common/assets/logo.svg

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="126px" height="108px" viewBox="0 0 42 36" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+    <!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch -->
+    <title>logo</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="logo" sketch:type="MSArtboardGroup">
+            <g sketch:type="MSLayerGroup" transform="translate(1.000000, 0.000000)" id="Shape">
+                <path d="M27.3375,12.6 L36.72,9.72 L31.1625,13.2525 L27.3375,12.6 Z" fill="#CA2317" sketch:type="MSShapeGroup"></path>
+                <path d="M33.0525,19.62 L31.1625,13.2525 L36.72,9.72 L35.055,15.435 L33.0525,19.62 Z" fill="#E84F83" sketch:type="MSShapeGroup"></path>
+                <path d="M39.465,7.9875 L38.43,9.72 L35.055,15.435 L36.72,9.72 L39.465,7.9875 Z" fill="#CA2317" sketch:type="MSShapeGroup"></path>
+                <path d="M39.8025,9.1125 L37.1925,11.79 L38.43,9.72 L39.8025,9.1125 Z" fill="#E54011" sketch:type="MSShapeGroup"></path>
+                <path d="M27.9,10.8225 L35.5725,10.0575 L30.24,11.7 L27.9,10.8225 Z" fill="#E54011" sketch:type="MSShapeGroup"></path>
+                <path d="M28.1925,15.165 L31.1625,13.2525 L33.0525,19.62 L32.0625,21.645 L28.1925,15.165 Z" fill="#CA2317" sketch:type="MSShapeGroup"></path>
+                <path d="M23.76,22.725 L22.3425,5.4 L32.0625,21.645 L23.76,22.725 Z" fill="#B7DFF2" sketch:type="MSShapeGroup"></path>
+                <path d="M19.7325,27.1575 L23.76,22.725 L32.0625,21.645 L19.7325,27.1575 Z" fill="#E54011" sketch:type="MSShapeGroup"></path>
+                <path d="M0.1575,35.865 L19.7325,27.1575 L23.76,22.725 L17.37,22.0725 L0.1575,35.865 Z" fill="#FFCE33" sketch:type="MSShapeGroup"></path>
+                <path d="M0.9,28.755 L10.9575,27.225 L14.085,24.705 L12.555,24.03 L0.9,28.755 Z" fill="#D6B12D" sketch:type="MSShapeGroup"></path>
+                <path d="M4.5225,20.5425 L14.085,24.705 L17.37,22.0725 L4.5225,20.5425 Z" fill="#FFDE85" sketch:type="MSShapeGroup"></path>
+                <path d="M21.6225,11.6775 L20.4075,11.88 L17.37,22.0725 L20.655,20.0025 L21.6225,11.6775 Z" fill="#009EC6" sketch:type="MSShapeGroup"></path>
+                <path d="M23.4,18.2475 L20.655,20.0025 L22.3425,5.4 L23.4,18.2475 Z" fill="#5EAFCE" sketch:type="MSShapeGroup"></path>
+                <path d="M13.0275,13.05 L21.6225,11.6775 L22.005,8.28 L13.0275,13.05 Z" fill="#045972" sketch:type="MSShapeGroup"></path>
+                <path d="M12.105,5.085 L19.575,9.585 L22.005,8.28 L22.0725,7.8075 L12.105,5.085 Z" fill="#5A8591" sketch:type="MSShapeGroup"></path>
+                <path d="M13.5675,0.18 L20.3625,7.335 L22.0725,7.8075 L22.3425,5.4 L13.5675,0.18 Z" fill="#009EC6" sketch:type="MSShapeGroup"></path>
+                <path d="M17.37,22.0725 L23.4,18.2475 L23.76,22.725 L17.37,22.0725 Z" fill="#F39804" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 0 - 0
app/frontend/common/components/CommonHelloWorld.vue → app/frontend/common/components/common/CommonHelloWorld.vue


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