init git
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<form class="otp-code">
|
||||
<h1>{{ $t("enterReceivedCode") }}</h1>
|
||||
<ul ref="list">
|
||||
<li v-for="(_, i) in 6" :key="i">
|
||||
<InputNumber v-model="code[i]" @input="events.input($event, i)" @keyup="events.keyup($event, i)"
|
||||
@focus="events.focus($event, i)" :disabled="inputs.length < 1" />
|
||||
</li>
|
||||
</ul>
|
||||
<span :class="{ invisible: seconds < 1 }">
|
||||
{{ $t("moreSecondReceiveCode", { seconds }) }}
|
||||
</span>
|
||||
<Button v-bind="btns.next.props" @click="events.next()" />
|
||||
<div v-show="seconds < 1">
|
||||
<p>{{ $t("notReceivedCode") }}</p>
|
||||
<Button v-bind="btns.try.props" @click="events.try()" />
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import init from "../../initialize/auth/otp-code.js";
|
||||
const { btns } = init();
|
||||
|
||||
const { t, toast } = inject('service')
|
||||
|
||||
let inputs = reactive([]);
|
||||
const list = ref();
|
||||
const code = ref([]);
|
||||
const seconds = ref(0);
|
||||
const form = computed(() => code.value.join(""));
|
||||
|
||||
const store = useOtpCodeStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const events = reactive({
|
||||
next: async () => {
|
||||
btns.next.props.loading = true
|
||||
if (form.value.length == 6) {
|
||||
const { status, error } = await store.check(form.value);
|
||||
if (status.value == "success")
|
||||
router.push({ query: { step: 2 } });
|
||||
else {
|
||||
code.value = []
|
||||
let detail = t("error-500");
|
||||
switch (error.value.statusCode) {
|
||||
case 400:
|
||||
detail = t("incorrect-code");
|
||||
break;
|
||||
case 429:
|
||||
detail = t("error-429");
|
||||
break;
|
||||
}
|
||||
toast.add({ severity: "error", summary: t("error"), detail, life: 3000 });
|
||||
}
|
||||
}
|
||||
btns.next.props.loading = false
|
||||
},
|
||||
try: async () => {
|
||||
btns.try.props.loading = true
|
||||
|
||||
const form = { number: store.number };
|
||||
let result = {}
|
||||
if (route.path.includes('register'))
|
||||
result = await store.withoutToken(form)
|
||||
else if (route.path.includes('restore'))
|
||||
result = await store.forgotPassword(form
|
||||
)
|
||||
const { status, error } = result;
|
||||
if (status.value == "success")
|
||||
seconds.value = 60;
|
||||
else
|
||||
toast.add({ severity: "error", summary: t("error"), detail: error.value.message, life: 3000 });
|
||||
btns.try.props.loading = false
|
||||
|
||||
},
|
||||
input: (e, i) => {
|
||||
if (e.value != null) inputs[i++].value = e.originalEvent.key;
|
||||
if (i < 6) inputs[i].focus();
|
||||
else {
|
||||
inputs[5].blur();
|
||||
events.next();
|
||||
}
|
||||
},
|
||||
keyup: (e, i) => {
|
||||
if (e.code == "Backspace" && i > 0) {
|
||||
code.value[i - 1] = null;
|
||||
inputs[i - 1].focus();
|
||||
}
|
||||
},
|
||||
focus: (e, i) => {
|
||||
const length = code.value.filter((c) => typeof c == "number").length;
|
||||
if (length < i) inputs[length].focus();
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
inputs.push(...list.value.querySelectorAll("input"));
|
||||
setTimeout(() => inputs[0].focus(), 1);
|
||||
seconds.value = 60;
|
||||
setInterval(() => {
|
||||
if (seconds.value > 0) seconds.value--;
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.auth .otp-code {
|
||||
@apply flex flex-col h-max gap-3 lg:gap-4 items-center mx-auto;
|
||||
|
||||
&>h1 {
|
||||
@apply w-full text-[#CCCCCC] font-black font-vazir text-right leading-[3rem] whitespace-nowrap;
|
||||
@apply text-xl lg:text-3xl lg:text-center;
|
||||
}
|
||||
|
||||
&>ul {
|
||||
@apply flex gap-2.5 mt-3 lg:mt-8 ltr;
|
||||
|
||||
li {
|
||||
input {
|
||||
@apply w-12 lg:w-10 h-10 p-0 rounded-lg border border-stone-300 text-center;
|
||||
@apply focus:text-primary-600 text-stone-300 text-xl font-medium font-vazir leading-[1.9rem];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>span {
|
||||
@apply text-[#999999] text-xs lg:text-sm font-medium font-vazir mt-1 lg:mt-0;
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply w-full h-11 mt-3 lg:h-14 lg:mt-6 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-sm font-medium font-iran-sans;
|
||||
}
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply flex items-center lg:mt-2 whitespace-nowrap;
|
||||
|
||||
p {
|
||||
@apply text-[#999999] text-xs lg:text-sm font-medium font-vazir;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply w-full px-1 h-11 lg:h-14 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-sm font-medium font-iran-sans;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<form class="completeInformation">
|
||||
<div>
|
||||
<ul>
|
||||
<li v-for="(item, key) in fields" :key="key">
|
||||
<component :is="item.is" :id="key" v-bind="item.props" v-model="form[key]" />
|
||||
<label :for="key"> {{ $t(key) }} </label>
|
||||
<small v-if="errors[key]">{{ errors[key] }}</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<Button v-bind="btn.props" @click="register()" />
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import iranCities from "../../initialize/iranCities"
|
||||
import init from "../../initialize/auth/information";
|
||||
const { fields, btn } = init();
|
||||
|
||||
const form = reactive({});
|
||||
const errors = ref({});
|
||||
|
||||
const otpCode = useOtpCodeStore();
|
||||
|
||||
onMounted(() => {
|
||||
form.phoneNumber = otpCode.number
|
||||
form.otp = otpCode.code
|
||||
})
|
||||
|
||||
const provinces = reactive(iranCities)
|
||||
fields.state.props.options = provinces
|
||||
|
||||
watch(() => form.state, (v) => {
|
||||
provinces.forEach(({ name, cities }) => {
|
||||
if (name == v) {
|
||||
fields.city.props.options = reactive(cities)
|
||||
}
|
||||
});
|
||||
}, { deep: true })
|
||||
|
||||
fields.city.props.disabled = computed(() => fields.city.props.options.length < 1)
|
||||
|
||||
const route = useRouter();
|
||||
const router = useRouter();
|
||||
|
||||
const store = useUserStore();
|
||||
|
||||
const register = async () => {
|
||||
errors.value = {}
|
||||
btn.props.loading = true
|
||||
const { status, error } = await store.register(Object.assign({}, form));
|
||||
if (status.value == "success") {
|
||||
delete store.profile.password;
|
||||
router.push("/panel/profile");
|
||||
} else {
|
||||
if (error.value.statusCode == 422)
|
||||
errors.value = error.value.data
|
||||
else
|
||||
toast.add({
|
||||
life: 50000,
|
||||
severity: 'error',
|
||||
summary: t('error'),
|
||||
detail: error.value.data.message || error.value.message
|
||||
})
|
||||
}
|
||||
btn.props.loading = false
|
||||
}
|
||||
|
||||
watch(form, () => errors.value = {})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.auth .completeInformation {
|
||||
@apply h-max flex flex-col m-auto;
|
||||
@apply lg:mt-6 lg:gap-14;
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply w-max overflow-y-auto pl-4 lg:pl-7;
|
||||
@apply h-[18.6rem] lg:h-[35.2rem];
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
@apply bg-transparent my-4;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
@apply w-1 lg:w-2;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
@apply bg-neutral-200;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply grid grid-cols-2 grow max-w-full py-4 px-px my-0;
|
||||
@apply gap-y-6 gap-x-2 w-[21.4rem] lg:w-[27.1rem] lg:gap-x-3;
|
||||
|
||||
li {
|
||||
@apply w-full grow h-14 relative col-span-2;
|
||||
|
||||
label {
|
||||
@apply w-max absolute right-3 top-4 px-1 bg-white transition-['top'];
|
||||
@apply text-neutral-400 text-base font-medium font-vazir cursor-text pointer-events-none;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
@apply w-full rounded-[0.6rem] border shadow-none border-[#CCCCCC] text-zinc-800 font-vazir;
|
||||
}
|
||||
|
||||
.p-dropdown-label {
|
||||
@apply font-vazir mt-1;
|
||||
}
|
||||
|
||||
&:has(input:focus),
|
||||
&:has(textarea:focus),
|
||||
&:has(input:not(:placeholder-shown)),
|
||||
&:has(.p-dropdown-label[aria-label]),
|
||||
&:has(textarea:not(:placeholder-shown)) {
|
||||
label {
|
||||
@apply -top-2 h-4;
|
||||
@apply text-zinc-800 text-xs font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-of-type(1),
|
||||
&:nth-of-type(2),
|
||||
&:nth-of-type(3),
|
||||
&:nth-of-type(4),
|
||||
&:nth-of-type(8),
|
||||
&:nth-of-type(9) {
|
||||
@apply w-full col-span-1 max-w-[12.7rem] lg:max-w-[13.1rem];
|
||||
}
|
||||
|
||||
.p-dropdown {
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
.p-calendar {
|
||||
@apply w-full flex items-center relative z-0 font-['iconsax'] #{!important};
|
||||
|
||||
&::before {
|
||||
@apply text-xl absolute mb-0.5 left-3 z-[1] text-[#999999];
|
||||
}
|
||||
|
||||
input {
|
||||
@apply pl-10;
|
||||
}
|
||||
}
|
||||
|
||||
small {
|
||||
@apply text-red-500 font-vazir float-left;
|
||||
}
|
||||
|
||||
&:has(small) * {
|
||||
@apply border-red-500 #{!important};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(2) {
|
||||
@apply py-3 -mr-6 px-5 w-full min-w-[24.4rem] border-t border-gray-100 shadow-[0px_-3px_5px_#00000005];
|
||||
@apply lg:border-0 lg:shadow-none lg:mr-0 lg:p-0;
|
||||
|
||||
&>button {
|
||||
@apply w-full h-11 lg:h-14 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-sm font-medium font-iran-sans;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<form class="password">
|
||||
<h1>{{ $t("chooseStrongPassword") }}</h1>
|
||||
<ul>
|
||||
<li v-for="(item, key) in fields" :key="key">
|
||||
<Password v-model="form[key]" v-bind="item.props" :inputId="key" />
|
||||
<label :for="key">{{ $t(key) }}</label>
|
||||
</li>
|
||||
</ul>
|
||||
<Button v-bind="btn.props" @click="next()" />
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import init from "../../initialize/auth/password";
|
||||
const { fields, btn } = init();
|
||||
const { t, toast } = inject('service')
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const form = reactive({ password: '', repassword: null });
|
||||
const store = useUserStore();
|
||||
const otpCode = useOtpCodeStore()
|
||||
|
||||
const next = async () => {
|
||||
if (form.password.length < 8) {
|
||||
toast.add({ severity: "error", summary: t("error"), detail: t('passwordMin8'), life: 3000 });
|
||||
} else if (form.password != form.repassword) {
|
||||
toast.add({ severity: "error", summary: t("error"), detail: t('passwordNotSame'), life: 3000 });
|
||||
} else {
|
||||
if (route.path.includes('register')) {
|
||||
store.profile.password = form.password
|
||||
router.push({ query: { step: 3 } });
|
||||
} else if (route.path.includes('restore')) {
|
||||
btn.props.loading = true
|
||||
const body = Object.assign({}, form)
|
||||
body.phoneNumber = otpCode.number
|
||||
body.otp = otpCode.code
|
||||
delete body.repassword
|
||||
const { status, error } = await store.forgetPassword(body);
|
||||
if (status.value == "success") {
|
||||
toast.add({ severity: "success", summary: t("success"), detail: t('updatePasswordSuccessfull'), life: 3000 });
|
||||
router.replace('/auth/login');
|
||||
|
||||
}
|
||||
else {
|
||||
let detail = error.value.data.msg
|
||||
toast.add({ severity: "error", summary: t("error"), detail, life: 3000 });
|
||||
}
|
||||
btn.props.loading = false
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.auth .password {
|
||||
@apply flex flex-col m-auto gap-[1.9rem] lg:gap-[3.2rem];
|
||||
|
||||
&>h1 {
|
||||
@apply w-full text-[#CCCCCC] font-black font-vazir text-right leading-[3rem] whitespace-nowrap;
|
||||
@apply text-xl lg:text-3xl;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex flex-col gap-6 lg:mb-7;
|
||||
|
||||
li {
|
||||
@apply w-full h-14 relative;
|
||||
|
||||
label {
|
||||
@apply w-max absolute right-3 top-4 px-1 bg-white transition-['top'];
|
||||
@apply text-neutral-400 text-base font-medium cursor-text font-vazir;
|
||||
}
|
||||
|
||||
.p-password {
|
||||
input {
|
||||
@apply w-full rounded-[0.6rem] border shadow-none border-[#CCCCCC] text-zinc-800 font-vazir;
|
||||
}
|
||||
|
||||
svg {
|
||||
@apply text-[#CCCCCC] w-5 h-5 -mt-2.5;
|
||||
}
|
||||
}
|
||||
|
||||
&:has(input:focus),
|
||||
&:has(input:not(:placeholder-shown)) {
|
||||
label {
|
||||
@apply -top-2 h-4;
|
||||
@apply text-zinc-800 text-xs font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
@apply w-full h-11 mt-1.5 lg:h-14 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-sm font-medium font-iran-sans;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<form class="phone">
|
||||
<h1>{{ $t("enterYourPhoneNumber") }}</h1>
|
||||
<div>
|
||||
<InputText v-bind="field.props" v-model="form.number" />
|
||||
<label for="phone"> {{ $t("phoneNumber") }} </label>
|
||||
</div>
|
||||
<Button v-bind="btn.props" @click="send()" />
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import init from "../../initialize/auth/phone";
|
||||
const { field, btn } = init();
|
||||
|
||||
const store = useOtpCodeStore();
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const form = reactive({ number: store.number });
|
||||
|
||||
const { toast, t } = inject('service')
|
||||
|
||||
const send = async () => {
|
||||
btn.props.loading = true;
|
||||
let result = {}
|
||||
const body = Object.assign({}, form);
|
||||
|
||||
if (route.path.includes('register'))
|
||||
result = await store.withoutToken(body)
|
||||
else if (route.path.includes('restore'))
|
||||
result = await store.forgotPassword(body
|
||||
)
|
||||
const { status, data, error } = result;
|
||||
btn.props.loading = false;
|
||||
|
||||
if (status.value == "success")
|
||||
router.push({ query: { step: 1 } });
|
||||
else {
|
||||
let detail = error.value.data.msg
|
||||
if (error.value.statusCode == 422)
|
||||
detail = Object.values(error.value.data).join('\n')
|
||||
|
||||
toast.add({ life: 3000, severity: 'error', summary: t('error'), detail })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.auth .phone {
|
||||
@apply flex flex-col h-max m-auto;
|
||||
@apply gap-[1.9rem] lg:gap-10;
|
||||
|
||||
&>h1 {
|
||||
@apply text-[#CCCCCC] font-black font-vazir text-right whitespace-nowrap;
|
||||
@apply text-xl lg:text-3xl leading-[3rem];
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply w-full h-14 relative;
|
||||
|
||||
label {
|
||||
@apply w-max absolute right-3 top-4 px-1 bg-white transition-['top'];
|
||||
@apply text-neutral-400 text-base font-medium font-vazir cursor-text;
|
||||
}
|
||||
|
||||
input {
|
||||
@apply w-full rounded-[0.6rem] border shadow-none border-[#CCCCCC] text-zinc-800 font-vazir;
|
||||
}
|
||||
|
||||
&:has(input:focus),
|
||||
&:has(input:not(:placeholder-shown)) {
|
||||
label {
|
||||
@apply -top-2 h-4;
|
||||
@apply text-zinc-800 text-xs font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
@apply w-full mt-1.5 h-11 lg:h-14 lg:mt-5 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-sm font-medium font-iran-sans;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user