|  | 
| 1 | 1 | <template> | 
| 2 | 2 |  <div> | 
| 3 |  | - <img class="mx-auto h-12 w-auto" src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg" | 
| 4 |  | - alt="Workflow"/> | 
|  | 3 | + <img | 
|  | 4 | + class="mx-auto h-12 w-auto" | 
|  | 5 | + src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg" | 
|  | 6 | + alt="Workflow" | 
|  | 7 | + /> | 
| 5 | 8 |  <h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900"> | 
| 6 | 9 |  Sign in to your account | 
| 7 | 10 |  </h2> | 
| 8 | 11 |  <p class="mt-2 text-center text-sm text-gray-600"> | 
| 9 | 12 |  Or | 
| 10 |  | - {{ ' ' }} | 
| 11 |  | - <router-link :to="{name: 'Register'}" class="font-medium text-indigo-600 hover:text-indigo-500"> | 
|  | 13 | + {{ " " }} | 
|  | 14 | + <router-link | 
|  | 15 | + :to="{ name: 'Register' }" | 
|  | 16 | + class="font-medium text-indigo-600 hover:text-indigo-500" | 
|  | 17 | + > | 
| 12 | 18 |  register for free | 
| 13 | 19 |  </router-link> | 
| 14 | 20 |  </p> | 
| 15 | 21 |  </div> | 
| 16 | 22 |  <form class="mt-8 space-y-6" @submit="login"> | 
| 17 |  | - <div v-if="errorMsg" class="flex items-center justify-between py-3 px-5 bg-red-500 text-white rounded"> | 
|  | 23 | + <div | 
|  | 24 | + v-if="errorMsg" | 
|  | 25 | + class="flex items-center justify-between py-3 px-5 bg-red-500 text-white rounded" | 
|  | 26 | + > | 
| 18 | 27 |  {{ errorMsg }} | 
| 19 |  | - <span @click="errorMsg = ''" class="w-8 h-8 flex items-center justify-center rounded-full transition-colors cursor-pointer hover:bg-[rgba(0,0,0,0.2)]"> | 
| 20 |  | - <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | 
| 21 |  | - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/> | 
|  | 28 | + <span | 
|  | 29 | + @click="errorMsg = ''" | 
|  | 30 | + class="w-8 h-8 flex items-center justify-center rounded-full transition-colors cursor-pointer hover:bg-[rgba(0,0,0,0.2)]" | 
|  | 31 | + > | 
|  | 32 | + <svg | 
|  | 33 | + xmlns="http://www.w3.org/2000/svg" | 
|  | 34 | + class="h-6 w-6" | 
|  | 35 | + fill="none" | 
|  | 36 | + viewBox="0 0 24 24" | 
|  | 37 | + stroke="currentColor" | 
|  | 38 | + > | 
|  | 39 | + <path | 
|  | 40 | + stroke-linecap="round" | 
|  | 41 | + stroke-linejoin="round" | 
|  | 42 | + stroke-width="2" | 
|  | 43 | + d="M6 18L18 6M6 6l12 12" | 
|  | 44 | + /> | 
| 22 | 45 |  </svg> | 
| 23 | 46 |  </span> | 
| 24 | 47 |  </div> | 
| 25 |  | - <input type="hidden" name="remember" value="true"/> | 
|  | 48 | + <input type="hidden" name="remember" value="true" /> | 
| 26 | 49 |  <div class="rounded-md shadow-sm -space-y-px"> | 
| 27 | 50 |  <div> | 
| 28 | 51 |  <label for="email-address" class="sr-only">Email address</label> | 
| 29 |  | - <input id="email-address" name="email" type="email" autocomplete="email" required="" v-model="user.email" | 
| 30 |  | - class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" | 
| 31 |  | - placeholder="Email address"/> | 
|  | 52 | + <input | 
|  | 53 | + id="email-address" | 
|  | 54 | + name="email" | 
|  | 55 | + type="email" | 
|  | 56 | + autocomplete="email" | 
|  | 57 | + required="" | 
|  | 58 | + v-model="user.email" | 
|  | 59 | + class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" | 
|  | 60 | + placeholder="Email address" | 
|  | 61 | + /> | 
| 32 | 62 |  </div> | 
| 33 | 63 |  <div> | 
| 34 | 64 |  <label for="password" class="sr-only">Password</label> | 
| 35 |  | - <input id="password" name="password" type="password" autocomplete="current-password" required="" | 
| 36 |  | - v-model="user.password" | 
| 37 |  | - class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" | 
| 38 |  | - placeholder="Password"/> | 
|  | 65 | + <input | 
|  | 66 | + id="password" | 
|  | 67 | + name="password" | 
|  | 68 | + type="password" | 
|  | 69 | + autocomplete="current-password" | 
|  | 70 | + required="" | 
|  | 71 | + v-model="user.password" | 
|  | 72 | + class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" | 
|  | 73 | + placeholder="Password" | 
|  | 74 | + /> | 
| 39 | 75 |  </div> | 
| 40 | 76 |  </div> | 
| 41 | 77 | 
 | 
| 42 | 78 |  <div class="flex items-center justify-between"> | 
| 43 | 79 |  <div class="flex items-center"> | 
| 44 |  | - <input id="remember-me" name="remember-me" type="checkbox" v-model="user.remember" | 
| 45 |  | - class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"/> | 
|  | 80 | + <input | 
|  | 81 | + id="remember-me" | 
|  | 82 | + name="remember-me" | 
|  | 83 | + type="checkbox" | 
|  | 84 | + v-model="user.remember" | 
|  | 85 | + class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" | 
|  | 86 | + /> | 
| 46 | 87 |  <label for="remember-me" class="ml-2 block text-sm text-gray-900"> | 
| 47 | 88 |  Remember me | 
| 48 | 89 |  </label> | 
| 49 | 90 |  </div> | 
| 50 |  | - | 
| 51 | 91 |  </div> | 
| 52 | 92 | 
 | 
| 53 | 93 |  <div> | 
| 54 |  | - <button type="submit" | 
| 55 |  | - class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | 
| 56 |  | - <span class="absolute left-0 inset-y-0 flex items-center pl-3"> | 
| 57 |  | - <LockClosedIcon class="h-5 w-5 text-indigo-500 group-hover:text-indigo-400" aria-hidden="true"/> | 
| 58 |  | - </span> | 
|  | 94 | + <button | 
|  | 95 | + type="submit" | 
|  | 96 | + :disabled="loading" | 
|  | 97 | + class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" | 
|  | 98 | + :class="{ 'cursor-not-allowed': loading, 'hover:bg-indigo-500': loading }" | 
|  | 99 | + > | 
|  | 100 | + <span class="absolute left-0 inset-y-0 flex items-center pl-3"> | 
|  | 101 | + <LockClosedIcon | 
|  | 102 | + class="h-5 w-5 text-indigo-500 group-hover:text-indigo-400" | 
|  | 103 | + aria-hidden="true" | 
|  | 104 | + /> | 
|  | 105 | + </span> | 
|  | 106 | + <svg | 
|  | 107 | + v-if="loading" | 
|  | 108 | + class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" | 
|  | 109 | + xmlns="http://www.w3.org/2000/svg" | 
|  | 110 | + fill="none" | 
|  | 111 | + viewBox="0 0 24 24" | 
|  | 112 | + > | 
|  | 113 | + <circle | 
|  | 114 | + class="opacity-25" | 
|  | 115 | + cx="12" | 
|  | 116 | + cy="12" | 
|  | 117 | + r="10" | 
|  | 118 | + stroke="currentColor" | 
|  | 119 | + stroke-width="4" | 
|  | 120 | + ></circle> | 
|  | 121 | + <path | 
|  | 122 | + class="opacity-75" | 
|  | 123 | + fill="currentColor" | 
|  | 124 | + d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" | 
|  | 125 | + ></path> | 
|  | 126 | + </svg> | 
| 59 | 127 |  Sign in | 
| 60 | 128 |  </button> | 
| 61 | 129 |  </div> | 
| 62 | 130 |  </form> | 
| 63 | 131 | </template> | 
| 64 | 132 | 
 | 
| 65 | 133 | <script setup> | 
| 66 |  | -import {LockClosedIcon} from '@heroicons/vue/solid' | 
|  | 134 | +import { LockClosedIcon } from "@heroicons/vue/solid"; | 
| 67 | 135 | import store from "../store"; | 
| 68 |  | -import {useRouter} from "vue-router"; | 
| 69 |  | -import {ref} from "vue"; | 
|  | 136 | +import { useRouter } from "vue-router"; | 
|  | 137 | +import { ref } from "vue"; | 
| 70 | 138 | 
 | 
| 71 | 139 | const router = useRouter(); | 
| 72 | 140 | 
 | 
| 73 | 141 | const user = { | 
| 74 |  | - email: '', | 
| 75 |  | - password: '' | 
| 76 |  | -} | 
| 77 |  | -let errorMsg = ref(''); | 
|  | 142 | + email: "", | 
|  | 143 | + password: "", | 
|  | 144 | +}; | 
|  | 145 | +let loading = ref(false); | 
|  | 146 | +let errorMsg = ref(""); | 
| 78 | 147 | 
 | 
| 79 | 148 | function login(ev) { | 
| 80 | 149 |  ev.preventDefault(); | 
| 81 | 150 | 
 | 
| 82 |  | - store.dispatch('login', user) | 
|  | 151 | + loading.value = true; | 
|  | 152 | + store | 
|  | 153 | + .dispatch("login", user) | 
| 83 | 154 |  .then(() => { | 
|  | 155 | + loading.value = false; | 
| 84 | 156 |  router.push({ | 
| 85 |  | - name: 'Dashboard' | 
| 86 |  | - }) | 
|  | 157 | + name: "Dashboard", | 
|  | 158 | + }); | 
| 87 | 159 |  }) | 
| 88 |  | - .catch(err => { | 
| 89 |  | - errorMsg.value = err.response.data.error | 
| 90 |  | - }) | 
| 91 |  | -
 | 
|  | 160 | + .catch((err) => { | 
|  | 161 | + loading.value = false; | 
|  | 162 | +  errorMsg.value = err.response.data.error; | 
|  | 163 | + }); | 
| 92 | 164 | } | 
| 93 |  | -
 | 
| 94 | 165 | </script> | 
0 commit comments