changed route folder
This commit is contained in:
@@ -1,88 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
import useGetCategories from "~/composables/api/product/useGetCategories";
|
||||
|
||||
// state
|
||||
|
||||
const { data: categories, suspense } = useGetCategories();
|
||||
|
||||
const search = ref("");
|
||||
const debouncedSearch = refDebounced(search, 300);
|
||||
|
||||
// computed
|
||||
|
||||
const filteredCategories = computed(() => {
|
||||
if (debouncedSearch.value.length > 0) {
|
||||
return categories.value!.filter(cat => cat.name.includes(debouncedSearch.value));
|
||||
}
|
||||
|
||||
return categories.value!;
|
||||
});
|
||||
|
||||
// lifecycle
|
||||
|
||||
onServerPrefetch(async () => {
|
||||
const response = await suspense();
|
||||
|
||||
if (response.isError) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: `Error in categories page prefetch`
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="mt-20 flex gap-6 justify-between items-center">
|
||||
<span class="typo-h-3 text-black">دسته بندی ها</span>
|
||||
<Input
|
||||
class="max-w-[400px] w-full"
|
||||
variant="outlined"
|
||||
placeholder="جستجو..."
|
||||
v-model="search"
|
||||
>
|
||||
<template #endItem>
|
||||
<div class="flex items-center gap-1">
|
||||
<Icon class="translate-y-[-1px]" name="ci:search" size="24" />
|
||||
</div>
|
||||
</template>
|
||||
</Input>
|
||||
</div>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div
|
||||
v-if="filteredCategories.length !== 0"
|
||||
v-auto-animate
|
||||
class="grid grid-cols-3 gap-4 w-full mt-12"
|
||||
>
|
||||
<CategoryCard
|
||||
v-for="category in filteredCategories"
|
||||
:key="category.id"
|
||||
:id="category.id"
|
||||
:category="category.name"
|
||||
picture="/img/product-1.jpg"
|
||||
:count="20"
|
||||
description="یک دسته بندی تستasdasd"
|
||||
dark-layer
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="flex w-full mt-12"
|
||||
>
|
||||
<div
|
||||
class="flex-col flex-grow py-[12rem] gap-6 border-2 border-slate-200 border-dashed size-full rounded-100 flex-center"
|
||||
>
|
||||
<Icon name="bi:search" size="50" class="**:fill-gray-500" />
|
||||
<span class="text-lg text-gray-500">
|
||||
دسته بندی یافت نشد :(
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,234 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
// import
|
||||
|
||||
import { helpers, required } from "@vuelidate/validators";
|
||||
import { useVuelidate } from "@vuelidate/core";
|
||||
import useOtp from "~/composables/api/auth/useOtp";
|
||||
import useSignIn from "~/composables/api/auth/useSignIn";
|
||||
import { definePageMeta } from "#imports";
|
||||
import useGetAccount from "~/composables/api/account/useGetAccount";
|
||||
import { useAuth } from "~/composables/api/auth/useAuth";
|
||||
import { useToast } from "~/composables/global/useToast";
|
||||
import { useTimer } from "~/composables/global/useTimer";
|
||||
|
||||
// types
|
||||
|
||||
type LoginInfo = {
|
||||
phone: string;
|
||||
};
|
||||
|
||||
// meta
|
||||
|
||||
definePageMeta({
|
||||
middleware: ["check-is-not-logged-in"]
|
||||
});
|
||||
|
||||
// state
|
||||
|
||||
const { addToast } = useToast();
|
||||
|
||||
const { updateToken, updateRefreshToken } = useAuth();
|
||||
|
||||
const { refetch: refetchAccount } = useGetAccount();
|
||||
|
||||
const showOtp = ref(false);
|
||||
const otpCode = ref([]);
|
||||
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
phone: {
|
||||
required: helpers.withMessage("Phone is required", required),
|
||||
phoneValidator: helpers.regex(/^[1-9][0-9]{9}$/)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const loginInfo = ref<LoginInfo>({
|
||||
phone: ""
|
||||
});
|
||||
|
||||
const formValidator$ = useVuelidate(formRules, loginInfo);
|
||||
|
||||
const {
|
||||
timer: otpBlockerTimePassed,
|
||||
start: startOtpBlocker,
|
||||
reset: resetOtpBlocker,
|
||||
isPending: isResendOtpBlocked
|
||||
} = useTimer({
|
||||
duration: 5000
|
||||
});
|
||||
|
||||
const { mutateAsync: sendOtp, isPending: sendOtpIsPending } = useOtp();
|
||||
const { mutateAsync: signIn, isPending: signInIsPending, status: signInStatus } = useSignIn();
|
||||
|
||||
// computed
|
||||
|
||||
const sendOtpHandler = async () => {
|
||||
if (!sendOtpIsPending.value) {
|
||||
try {
|
||||
await sendOtp({
|
||||
phone: `0${loginInfo.value.phone}`
|
||||
});
|
||||
|
||||
addToast({
|
||||
message: "کد برای شما ارسال شد",
|
||||
options: {
|
||||
status: "success"
|
||||
}
|
||||
});
|
||||
|
||||
showOtp.value = true;
|
||||
|
||||
} catch (e) {
|
||||
addToast({
|
||||
message: "مشکلی پیش آمده",
|
||||
options: {
|
||||
status: "error"
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogin = async () => {
|
||||
if (!showOtp.value) {
|
||||
await formValidator$.value.$validate();
|
||||
|
||||
if (!formValidator$.value.$errors.length) {
|
||||
await sendOtpHandler();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const response = await signIn({
|
||||
otp: otpCode.value.join(""),
|
||||
phone: `0${loginInfo.value.phone}`
|
||||
});
|
||||
|
||||
updateToken(response.access);
|
||||
updateRefreshToken(response.refresh);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await refetchAccount();
|
||||
|
||||
addToast({
|
||||
message: "با موفقیت وارد شدید",
|
||||
options: {
|
||||
status: "success"
|
||||
}
|
||||
});
|
||||
|
||||
navigateTo("/");
|
||||
} catch (e) {
|
||||
otpCode.value = [];
|
||||
addToast({ message: "مشکلی پیش آمده" });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const resendOtp = async () => {
|
||||
try {
|
||||
await sendOtpHandler();
|
||||
resetOtpBlocker();
|
||||
startOtpBlocker();
|
||||
} catch (e) {
|
||||
resetOtpBlocker();
|
||||
}
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
loginInfo.value.phone = "";
|
||||
formValidator$.value.$reset();
|
||||
otpCode.value = [];
|
||||
showOtp.value = false;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container min-h-[700px] flex flex-col items-center justify-center">
|
||||
<h1 class="typo-hero-2">
|
||||
فرم ورود
|
||||
</h1>
|
||||
<form
|
||||
@submit.prevent
|
||||
class="max-w-[500px] w-full mt-12"
|
||||
>
|
||||
<div
|
||||
v-if="!showOtp"
|
||||
class="flex items-center gap-2 w-full"
|
||||
>
|
||||
<Input
|
||||
class="w-full"
|
||||
v-model="loginInfo.phone"
|
||||
placeholder="9380123456"
|
||||
dir="ltr"
|
||||
:error="formValidator$.phone.$error"
|
||||
>
|
||||
<template #startItem>
|
||||
<span class="text-slate-500">
|
||||
+98
|
||||
</span>
|
||||
</template>
|
||||
</Input>
|
||||
<div class="flex items-center gap-1">
|
||||
<Icon class="translate-y-[-1px]" name="twemoji:flag-iran" size="24" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<OtpInput
|
||||
v-else
|
||||
v-model="otpCode"
|
||||
:status="signInStatus === 'success' ? 'success' : signInStatus === 'error' ? 'error' : 'idle'"
|
||||
:disabled="signInIsPending || sendOtpIsPending"
|
||||
:autofocus="true"
|
||||
@complete="handleLogin"
|
||||
/>
|
||||
|
||||
<Button
|
||||
v-if="!showOtp"
|
||||
class="rounded-full w-full mt-4"
|
||||
type="submit"
|
||||
@click="handleLogin"
|
||||
:loading="sendOtpIsPending"
|
||||
:disabled="sendOtpIsPending"
|
||||
>
|
||||
ارسال کد
|
||||
</Button>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="flex items-center w-full gap-4 mt-4"
|
||||
>
|
||||
<Button
|
||||
class="rounded-full w-full mt-4"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
@click="resetForm"
|
||||
:disabled="signInIsPending || sendOtpIsPending"
|
||||
>
|
||||
تغییر شماره
|
||||
</Button>
|
||||
<Button
|
||||
class="rounded-full w-full mt-4"
|
||||
type="submit"
|
||||
@click="resendOtp"
|
||||
:loading="signInIsPending || sendOtpIsPending"
|
||||
:disabled="signInIsPending || isResendOtpBlocked || sendOtpIsPending"
|
||||
>
|
||||
ارسال مجدد کد
|
||||
{{ isResendOtpBlocked ? otpBlockerTimePassed : "" }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 justify-center mt-6">
|
||||
<span>
|
||||
بازگشت به فروشگاه
|
||||
</span>
|
||||
<Icon
|
||||
name="ci:left-rotation"
|
||||
size="24"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user