Re-design login view and implement login component.
authorAdam Dullage <redacted>
Thu, 4 Aug 2022 11:51:16 +0000 (12:51 +0100)
committerAdam Dullage <redacted>
Thu, 4 Aug 2022 11:51:16 +0000 (12:51 +0100)
flatnotes/src/components/App.js
flatnotes/src/components/App.vue
flatnotes/src/components/Login.vue [new file with mode: 0644]
flatnotes/src/constants.js
flatnotes/src/index.html

index 4a0b6740d1468a7c6b002a19efcf6da7a6079679..81549cca184e1ee808808a476148b252b9787e83 100644 (file)
@@ -3,6 +3,7 @@ import { Viewer } from "@toast-ui/vue-editor";
 
 import RecentlyModified from "./RecentlyModified";
 import LoadingIndicator from "./LoadingIndicator";
+import Login from "./Login";
 
 import api from "../api";
 import * as constants from "../constants";
@@ -16,6 +17,7 @@ export default {
     Editor,
     RecentlyModified,
     LoadingIndicator,
+    Login,
   },
 
   data: function() {
@@ -84,43 +86,6 @@ export default {
         (pageTitleSuffix ? `${pageTitleSuffix} - ` : "") + "flatnotes";
     },
 
-    login: function() {
-      let parent = this;
-      api
-        .post("/api/token", {
-          username: this.usernameInput,
-          password: this.passwordInput,
-        })
-        .then(function(response) {
-          sessionStorage.setItem("token", response.data.access_token);
-          if (parent.rememberMeInput == true) {
-            localStorage.setItem("token", response.data.access_token);
-          }
-          let redirectPath = helpers.getSearchParam(constants.params.redirect);
-          parent.navigate(redirectPath || "/");
-        })
-        .catch(function(error) {
-          if (error.handled) {
-            return;
-          } else if (
-            typeof error.response !== "undefined" &&
-            [400, 422].includes(error.response.status)
-          ) {
-            parent.$bvToast.toast("Incorrect Username or Password ✘", {
-              variant: "danger",
-              noCloseButton: true,
-            });
-          } else {
-            parent.unhandledServerErrorToast();
-          }
-        })
-        .finally(function() {
-          parent.usernameInput = null;
-          parent.passwordInput = null;
-          parent.rememberMeInput = false;
-        });
-    },
-
     logout: function() {
       sessionStorage.removeItem("token");
       localStorage.removeItem("token");
index 60bf9dc84d379abe8fd6658e816350112a28488b..23e8e62f35246b840ab56efec6861ec63b72fdbe 100644 (file)
 <template>
-  <div class="container">
-    <div>
-      <!-- Header -->
-      <div class="mt-4 mb-4">
-        <h1 class="h1 clickable-link text-center">
-          <a href="/" @click.prevent="navigate('/', $event)"
-            ><img src="../assets/logo.svg"
-          /></a>
-        </h1>
-      </div>
+  <div class="container h-100">
+    <!-- Header -->
+    <div v-if="currentView != views.login" class="mt-4 mb-4">
+      <a
+        href="/"
+        @click.prevent="navigate('/', $event)"
+        class="h1 clickable-link text-center"
+        ><img src="../assets/logo.svg"
+      /></a>
+    </div>
 
-      <!-- Login -->
-      <div
-        v-if="currentView == views.login"
-        class="d-flex justify-content-center"
+    <!-- Login -->
+    <Login v-if="currentView == views.login"></Login>
+
+    <!-- Buttons -->
+    <div class="d-flex justify-content-center mb-4">
+      <!-- Logout -->
+      <button
+        v-if="currentView == views.home"
+        type="button"
+        class="btn btn-sm btn-outline-dark mx-1"
+        @click="logout"
       >
-        <form v-on:submit.prevent="login">
-          <div class="mb-3">
-            <label for="username" class="form-label">Username</label>
-            <input
-              type="text"
-              class="form-control"
-              id="username"
-              autocomplete="username"
-              v-model="usernameInput"
-              required
-            />
-          </div>
-          <div class="mb-3">
-            <label for="password" class="form-label">Password</label>
-            <input
-              type="password"
-              class="form-control"
-              id="password"
-              autocomplete="current-password"
-              v-model="passwordInput"
-              required
-            />
-          </div>
-          <div class="mb-3 form-check">
-            <input
-              type="checkbox"
-              class="form-check-input"
-              id="rememberMe"
-              v-model="rememberMeInput"
-            />
-            <label class="form-check-label" for="rememberMe">Remember Me</label>
-          </div>
-          <button type="submit" class="btn btn-primary">Log In</button>
-        </form>
-      </div>
+        Logout
+      </button>
+
+      <!-- New -->
+      <button
+        v-if="currentView == views.home"
+        type="button"
+        class="btn btn-sm btn-outline-primary mx-1"
+        @click="newNote"
+      >
+        New
+      </button>
+
+      <!-- Edit -->
+      <button
+        v-if="
+          currentView == views.note &&
+          editMode == false &&
+          noteLoadFailed == false
+        "
+        type="button"
+        class="btn btn-sm btn-outline-warning mx-1"
+        @click="toggleEditMode"
+      >
+        Edit
+      </button>
+
+      <!-- Delete -->
+      <button
+        v-if="
+          currentView == views.note &&
+          editMode == false &&
+          noteLoadFailed == false
+        "
+        type="button"
+        class="btn btn-sm btn-outline-danger mx-1"
+        @click="deleteNote"
+      >
+        Delete
+      </button>
+
+      <!-- Cancel -->
+      <button
+        v-if="currentView == views.note && editMode == true"
+        type="button"
+        class="btn btn-sm btn-outline-secondary mx-1"
+        @click="cancelNote"
+      >
+        Cancel
+      </button>
+
+      <!-- Save -->
+      <button
+        v-if="currentView == views.note && editMode == true"
+        type="button"
+        class="btn btn-sm btn-outline-success mx-1"
+        @click="saveNote"
+      >
+        Save
+      </button>
+    </div>
 
-      <!-- Buttons -->
-      <div v-if="currentView != 4" class="d-flex justify-content-center mb-4">
-        <!-- Logout -->
-        <button
-          v-if="currentView == views.home"
-          type="button"
-          class="btn btn-sm btn-outline-dark mx-1"
-          @click="logout"
-        >
-          Logout
-        </button>
-
-        <!-- New -->
-        <button
-          v-if="currentView == views.home"
-          type="button"
-          class="btn btn-sm btn-outline-primary mx-1"
-          @click="newNote"
-        >
-          New
-        </button>
-
-        <!-- Close -->
-        <a href="/" @click.prevent="navigate('/')">
-          <button
-            v-if="currentView == 2 && editMode == false"
-            type="button"
-            class="btn btn-sm btn-outline-secondary mx-1"
-          >
-            Close
-          </button>
-        </a>
-
-        <!-- Edit -->
-        <button
-          v-if="
-            currentView == views.note &&
-            editMode == false &&
-            noteLoadFailed == false
-          "
-          type="button"
-          class="btn btn-sm btn-outline-warning mx-1"
-          @click="toggleEditMode"
-        >
-          Edit
-        </button>
-
-        <!-- Delete -->
-        <button
-          v-if="
-            currentView == views.note &&
-            editMode == false &&
-            noteLoadFailed == false
-          "
-          type="button"
-          class="btn btn-sm btn-outline-danger mx-1"
-          @click="deleteNote"
-        >
-          Delete
-        </button>
-
-        <!-- Cancel -->
-        <button
-          v-if="currentView == views.note && editMode == true"
-          type="button"
-          class="btn btn-sm btn-outline-secondary mx-1"
-          @click="cancelNote"
-        >
-          Cancel
-        </button>
-
-        <!-- Save -->
-        <button
-          v-if="currentView == views.note && editMode == true"
-          type="button"
-          class="btn btn-sm btn-outline-success mx-1"
-          @click="saveNote"
-        >
-          Save
-        </button>
+    <!-- Search Input -->
+    <form
+      v-if="[views.search, views.home].includes(currentView)"
+      v-on:submit.prevent="search"
+    >
+      <div class="form-group mb-4 d-flex justify-content-center">
+        <input
+          type="text"
+          class="form-control"
+          placeholder="Search"
+          v-model="searchTerm"
+          style="max-width: 500px"
+          autofocus
+        />
+      </div>
+    </form>
+
+    <!-- Note -->
+    <div v-if="currentView == views.note">
+      <!-- Loading -->
+      <div v-if="currentNote == null">
+        <loading-indicator
+          :failure-message="noteLoadFailedMessage"
+          :failed="noteLoadFailed"
+        />
       </div>
 
-      <!-- Search Input -->
-      <form
-        v-if="[views.search, views.home].includes(currentView)"
-        v-on:submit.prevent="search"
-      >
-        <div class="form-group mb-4 d-flex justify-content-center">
-          <input
-            type="text"
-            class="form-control"
-            placeholder="Search"
-            v-model="searchTerm"
-            style="max-width: 500px"
-          />
-        </div>
-      </form>
-
-      <!-- Note -->
-      <div v-if="currentView == views.note">
-        <!-- Loading -->
-        <div v-if="currentNote == null">
-          <loading-indicator
-            :failure-message="noteLoadFailedMessage"
-            :failed="noteLoadFailed"
-          />
-        </div>
+      <!-- Note Loaded -->
+      <div v-else>
+        <h2 v-if="editMode == false" class="mb-4">{{ currentNote.title }}</h2>
+        <input
+          v-else
+          type="text"
+          class="h2 title-input"
+          v-model="titleInput"
+          placeholder="Title"
+        />
+
+        <!-- Viewer -->
+        <div class="mb-4 note">
+          <div v-if="editMode == false" class="note-viewer">
+            <viewer
+              :initialValue="currentNote.content"
+              height="600px"
+              :options="viewerOptions"
+            />
+          </div>
 
-        <!-- Note Loaded -->
-        <div v-else>
-          <h2 v-if="editMode == false" class="mb-4">{{ currentNote.title }}</h2>
-          <input
-            v-else
-            type="text"
-            class="h2 title-input"
-            v-model="titleInput"
-            placeholder="Title"
-          />
-
-          <!-- Viewer -->
-          <div class="mb-4 note">
-            <div v-if="editMode == false" class="note-viewer">
-              <viewer
-                :initialValue="currentNote.content"
-                height="600px"
-                :options="viewerOptions"
-              />
-            </div>
-
-            <!-- Editor -->
-            <div v-else>
-              <editor
-                :initialValue="initialContent"
-                initialEditType="markdown"
-                previewStyle="tab"
-                height="calc(100vh - 230px)"
-                ref="toastUiEditor"
-                :options="editorOptions"
-                @change="startDraftSaveTimeout"
-              />
-            </div>
+          <!-- Editor -->
+          <div v-else>
+            <editor
+              :initialValue="initialContent"
+              initialEditType="markdown"
+              previewStyle="tab"
+              height="calc(100vh - 230px)"
+              ref="toastUiEditor"
+              :options="editorOptions"
+              @change="startDraftSaveTimeout"
+            />
           </div>
         </div>
       </div>
+    </div>
 
-      <!-- Search -->
-      <div v-if="currentView == views.search">
-        <!-- Searching -->
-        <div v-if="searchResults == null">
-          <loading-indicator
-            loading-message="Searching..."
-            failure-message="Search failed 😞"
-            :failed="searchFailed"
-          />
-        </div>
+    <!-- Search -->
+    <div v-if="currentView == views.search">
+      <!-- Searching -->
+      <div v-if="searchResults == null">
+        <loading-indicator
+          loading-message="Searching..."
+          failure-message="Search failed 😞"
+          :failed="searchFailed"
+        />
+      </div>
 
-        <!-- No Results -->
-        <div v-else-if="searchResults.length == 0">
-          <p class="text-center">No Results</p>
-        </div>
+      <!-- No Results -->
+      <div v-else-if="searchResults.length == 0">
+        <p class="text-center">No Results</p>
+      </div>
 
-        <!-- Search Results Loaded -->
-        <div v-else>
-          <div v-for="result in searchResults" :key="result.title" class="mb-5">
-            <p class="h5 text-center clickable-link">
-              <a
-                v-html="result.titleHighlightsOrTitle"
-                :href="result.href"
-                @click.prevent="navigate(result.href, $event)"
-              ></a>
-            </p>
-            <p
-              class="text-center text-muted"
-              v-html="result.contentHighlights"
-            ></p>
-          </div>
+      <!-- Search Results Loaded -->
+      <div v-else>
+        <div v-for="result in searchResults" :key="result.title" class="mb-5">
+          <p class="h5 text-center clickable-link">
+            <a
+              v-html="result.titleHighlightsOrTitle"
+              :href="result.href"
+              @click.prevent="navigate(result.href, $event)"
+            ></a>
+          </p>
+          <p
+            class="text-center text-muted"
+            v-html="result.contentHighlights"
+          ></p>
         </div>
       </div>
-
-      <!-- Home -->
-      <RecentlyModified v-if="currentView == views.home" />
     </div>
+
+    <!-- Home -->
+    <RecentlyModified v-if="currentView == views.home" />
   </div>
 </template>
 
diff --git a/flatnotes/src/components/Login.vue b/flatnotes/src/components/Login.vue
new file mode 100644 (file)
index 0000000..0f9ee7b
--- /dev/null
@@ -0,0 +1,112 @@
+<template>
+  <div
+    class="d-flex flex-column justify-content-center align-items-center h-100"
+  >
+    <!-- Logo -->
+    <div class="mb-3">
+      <img src="../assets/logo.svg" />
+    </div>
+
+    <form
+      class="d-flex flex-column align-items-center"
+      v-on:submit.prevent="login"
+    >
+      <!-- Username -->
+      <div class="mb-1">
+        <input
+          type="text"
+          placeholder="Username"
+          class="form-control"
+          id="username"
+          autocomplete="username"
+          v-model="usernameInput"
+          autofocus
+          required
+        />
+      </div>
+
+      <!-- Password -->
+      <div class="mb-2">
+        <input
+          type="password"
+          placeholder="Password"
+          class="form-control"
+          id="password"
+          autocomplete="current-password"
+          v-model="passwordInput"
+          required
+        />
+      </div>
+
+      <!-- Remember Me -->
+      <div class="mb-3 form-check">
+        <input
+          type="checkbox"
+          class="form-check-input"
+          id="rememberMe"
+          v-model="rememberMeInput"
+        />
+        <label class="form-check-label" for="rememberMe">Remember Me</label>
+      </div>
+
+      <!-- Button -->
+      <button type="submit" class="btn btn-primary">Log In</button>
+    </form>
+  </div>
+</template>
+
+<script>
+import api from "../api";
+import * as helpers from "../helpers";
+import * as constants from "../constants";
+import EventBus from "../eventBus";
+
+export default {
+  data: function () {
+    return {
+      usernameInput: null,
+      passwordInput: null,
+      rememberMeInput: false,
+    };
+  },
+
+  methods: {
+    login: function () {
+      let parent = this;
+      api
+        .post("/api/token", {
+          username: this.usernameInput,
+          password: this.passwordInput,
+        })
+        .then(function (response) {
+          sessionStorage.setItem("token", response.data.access_token);
+          if (parent.rememberMeInput == true) {
+            localStorage.setItem("token", response.data.access_token);
+          }
+          let redirectPath = helpers.getSearchParam(constants.params.redirect);
+          EventBus.$emit("navigate", redirectPath || "/");
+        })
+        .catch(function (error) {
+          if (error.handled) {
+            return;
+          } else if (
+            typeof error.response !== "undefined" &&
+            [400, 422].includes(error.response.status)
+          ) {
+            parent.$bvToast.toast("Incorrect Username or Password ✘", {
+              variant: "danger",
+              noCloseButton: true,
+            });
+          } else {
+            EventBus.$emit("unhandledServerError");
+          }
+        })
+        .finally(function () {
+          parent.usernameInput = null;
+          parent.passwordInput = null;
+          parent.rememberMeInput = false;
+        });
+    },
+  },
+};
+</script>
index 8f754229ca99c6ad6c3f1f751753eda8f8546f77..ffea29975bc91de271daa4020414c39f86000e41 100644 (file)
@@ -22,11 +22,6 @@ export const dataDefaults = function() {
     editMode: false,
     draftSaveTimeout: null,
 
-    // Login Data
-    usernameInput: null,
-    passwordInput: null,
-    rememberMeInput: false,
-
     // Search Data
     searchFailed: false,
 
index a205981057209be4dadeb62bebf27e59dbc18211..bb33b6452b655bdbfb26d4a1b1f4ccfe42e922cf 100644 (file)
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="en" class="h-100">
 
 <head>
   <meta charset="UTF-8">
@@ -20,7 +20,7 @@
 
 </head>
 
-<body>
+<body class="h-100">
   <div id="app"></div>
   <script src="index.js"></script>
 </body>
git clone https://git.99rst.org/PROJECT