Skip to content

Commit f2624f5

Browse files
committed
Create TInput.vue component and use in Register.vue
1 parent 91f9246 commit f2624f5

File tree

4 files changed

+114
-77
lines changed

4 files changed

+114
-77
lines changed

vue/src/axios.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ axiosClient.interceptors.response.use(response => {
2323
} else if (error.response.status === 404) {
2424
router.push({name: 'NotFound'})
2525
}
26-
return error;
26+
throw error;
2727
})
2828

2929
export default axiosClient;

vue/src/components/core/TInput.vue

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<template>
2+
<div>
3+
<label :for="id" class="sr-only">{{ label }}</label>
4+
<input
5+
:id="id"
6+
:type="type"
7+
:value="modelValue"
8+
@input="emits('update:modelValue', $event.target.value)"
9+
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
10+
:class="{ ...computedInputClass, 'border-red-500': errors[name], 'z-10': errors[email] }"
11+
:placeholder="placeholder"
12+
/>
13+
</div>
14+
</template>
15+
16+
<script setup>
17+
import { computed } from "@vue/reactivity";
18+
19+
const props = defineProps({
20+
type: {
21+
type: String,
22+
default: "text",
23+
},
24+
errors: Object,
25+
name: String,
26+
id: String,
27+
label: String,
28+
placeholder: String,
29+
modelValue: String,
30+
inputClass: [String, Array, Object],
31+
});
32+
33+
const emits = defineEmits(["update:modelValue"]);
34+
35+
const id = computed(
36+
() => props.id || "id-" + Math.floor(Math.random() * 100000000)
37+
);
38+
const computedInputClass = computed(() => {
39+
if (typeof props.inputClass === 'string') {
40+
return {
41+
[props.inputClass]: true
42+
}
43+
} else if (typeof props.inputClass === 'object' && props.inputClass.length !== undefined) {
44+
return props.inputClass.reduce((accum, val) => {
45+
accum[val] = true;
46+
return accum;
47+
}, {})
48+
}
49+
50+
return props.inputClass;
51+
})
52+
</script>
53+
54+
<style></style>

vue/src/store/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const store = createStore({
3232

3333
register({commit}, user) {
3434
return axiosClient.post('/register', user)
35-
.then(({data}) => {
35+
.then((response) => {
3636
commit('setUser', data.user);
3737
commit('setToken', data.token)
3838
return data;

vue/src/views/Register.vue

Lines changed: 58 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,88 @@
11
<template>
22
<div>
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-
/>
8-
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
9-
Register for free
10-
</h2>
11-
<p class="mt-2 text-center text-sm text-gray-600">
12-
Or
13-
{{ " " }}
14-
<router-link
15-
:to="{ name: 'Login' }"
16-
class="font-medium text-indigo-600 hover:text-indigo-500"
3+
<div>
4+
<img
5+
class="mx-auto h-12 w-auto"
6+
src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg"
7+
alt="Workflow"
8+
/>
9+
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
10+
Register for free
11+
</h2>
12+
<p class="mt-2 text-center text-sm text-gray-600">
13+
Or
14+
{{ " " }}
15+
<router-link
16+
:to="{ name: 'Login' }"
17+
class="font-medium text-indigo-600 hover:text-indigo-500"
18+
>
19+
login to your account
20+
</router-link>
21+
</p>
22+
</div>
23+
<form class="mt-8 space-y-6" @submit="register">
24+
<Alert
25+
v-if="Object.keys(errors).length"
26+
class="flex-col items-stretch text-sm"
1727
>
18-
login to your account
19-
</router-link>
20-
</p>
21-
</div>
22-
<form class="mt-8 space-y-6" @submit="register">
23-
<Alert v-if="Object.keys(errors).length" class="flex-col items-stretch text-sm">
24-
<div v-for="(field, i) of Object.keys(errors)" :key="i">
25-
<div v-for="(error, ind) of errors[field] || []" :key="ind">
26-
* {{ error }}
28+
<div v-for="(field, i) of Object.keys(errors)" :key="i">
29+
<div v-for="(error, ind) of errors[field] || []" :key="ind">
30+
* {{ error }}
31+
</div>
2732
</div>
28-
</div>
29-
</Alert>
33+
</Alert>
3034

31-
<input type="hidden" name="remember" value="true" />
32-
<div class="rounded-md shadow-sm -space-y-px">
33-
<div>
34-
<label for="fullname" class="sr-only">Email address</label>
35-
<input
36-
id="fullname"
35+
<input type="hidden" name="remember" value="true" />
36+
<div class="rounded-md shadow-sm -space-y-px">
37+
<TInput
3738
name="name"
38-
type="text"
39-
autocomplete="name"
40-
required=""
4139
v-model="user.name"
42-
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"
43-
placeholder="Full name"
40+
:errors="errors"
41+
placeholder="Full Name"
42+
inputClass="rounded-t-md"
4443
/>
45-
</div>
46-
<div>
47-
<label for="email-address" class="sr-only">Email address</label>
48-
<input
49-
id="email-address"
50-
name="email"
44+
<TInput
5145
type="email"
52-
autocomplete="email"
53-
required=""
46+
name="email"
5447
v-model="user.email"
55-
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
56-
:class="{ 'border-red-500': errors.email, 'z-10': errors.email }"
57-
placeholder="Email address"
48+
:errors="errors"
49+
placeholder="Email Address"
5850
/>
59-
</div>
60-
<div>
61-
<label for="password" class="sr-only">Password</label>
62-
<input
63-
id="password"
64-
name="password"
51+
<TInput
6552
type="password"
66-
autocomplete="current-password"
67-
required=""
53+
name="password"
6854
v-model="user.password"
69-
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
55+
:errors="errors"
7056
placeholder="Password"
71-
:class="{ 'border-red-500': errors.password, 'z-10': errors.password }"
7257
/>
73-
</div>
74-
<div>
75-
<label for="password_confirmation" class="sr-only">Password</label>
76-
<input
77-
id="password_confirmation"
78-
name="password_confirmation"
58+
<TInput
7959
type="password"
80-
autocomplete="current-password"
81-
required=""
60+
name="password_confirmation"
8261
v-model="user.password_confirmation"
83-
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"
62+
:errors="errors"
8463
placeholder="Confirm Password"
64+
inputClass="rounded-b-md"
8565
/>
8666
</div>
87-
</div>
88-
89-
<div>
90-
<TButtonLoading :loading="loading" class="w-full relative justify-center">
91-
Sign up
92-
</TButtonLoading>
93-
</div>
94-
</form>
67+
<div>
68+
<TButtonLoading
69+
:loading="loading"
70+
class="w-full relative justify-center"
71+
>
72+
Sign up
73+
</TButtonLoading>
74+
</div>
75+
</form>
76+
</div>
9577
</template>
9678

9779
<script setup>
9880
import { ref } from "vue";
9981
import { LockClosedIcon } from "@heroicons/vue/solid";
10082
import store from "../store";
10183
import { useRouter } from "vue-router";
102-
import TButtonLoading from '../components/core/TButtonLoading.vue'
84+
import TButtonLoading from "../components/core/TButtonLoading.vue";
85+
import TInput from "../components/core/TInput.vue";
10386
import Alert from "../components/Alert.vue";
10487
10588
const router = useRouter();

0 commit comments

Comments
 (0)