Skip to content

Commit 29f4da5

Browse files
committed
Implement CSS spinner on login form submit button
1 parent c0118ec commit 29f4da5

File tree

1 file changed

+110
-39
lines changed

1 file changed

+110
-39
lines changed

vue/src/views/Login.vue

Lines changed: 110 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,165 @@
11
<template>
22
<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+
/>
58
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
69
Sign in to your account
710
</h2>
811
<p class="mt-2 text-center text-sm text-gray-600">
912
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+
>
1218
register for free
1319
</router-link>
1420
</p>
1521
</div>
1622
<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+
>
1827
{{ 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+
/>
2245
</svg>
2346
</span>
2447
</div>
25-
<input type="hidden" name="remember" value="true"/>
48+
<input type="hidden" name="remember" value="true" />
2649
<div class="rounded-md shadow-sm -space-y-px">
2750
<div>
2851
<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+
/>
3262
</div>
3363
<div>
3464
<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+
/>
3975
</div>
4076
</div>
4177

4278
<div class="flex items-center justify-between">
4379
<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+
/>
4687
<label for="remember-me" class="ml-2 block text-sm text-gray-900">
4788
Remember me
4889
</label>
4990
</div>
50-
5191
</div>
5292

5393
<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>
59127
Sign in
60128
</button>
61129
</div>
62130
</form>
63131
</template>
64132

65133
<script setup>
66-
import {LockClosedIcon} from '@heroicons/vue/solid'
134+
import { LockClosedIcon } from "@heroicons/vue/solid";
67135
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";
70138
71139
const router = useRouter();
72140
73141
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("");
78147
79148
function login(ev) {
80149
ev.preventDefault();
81150
82-
store.dispatch('login', user)
151+
loading.value = true;
152+
store
153+
.dispatch("login", user)
83154
.then(() => {
155+
loading.value = false;
84156
router.push({
85-
name: 'Dashboard'
86-
})
157+
name: "Dashboard",
158+
});
87159
})
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+
});
92164
}
93-
94165
</script>

0 commit comments

Comments
 (0)