Files
hossein-por-shop/frontend/pages/profile/index.vue
T
2026-04-28 20:59:51 +03:30

277 lines
11 KiB
Vue

<script setup lang="ts">
// imports
import useVuelidate from "@vuelidate/core";
import { helpers, required, minLength, email } from "@vuelidate/validators";
import useGetAccount from "~/composables/api/account/useGetAccount";
import useUpdateAccount, { type UpdateAccountRequest } from "~/composables/api/account/useUpdateAccount";
import { useObjectTrack } from "~/composables/global/useObjectTrack";
import { useToast } from "~/composables/global/useToast";
import { QUERY_KEYS } from "~/constants";
// meta
useSeoMeta({
title: "پنل کاربری",
});
definePageMeta({
middleware: "check-is-logged-in",
layout: "profile",
});
// state
const { data: account } = useGetAccount();
const { $queryClient: queryClient } = useNuxtApp();
const { addToast } = useToast();
const personalData = ref<UpdateAccountRequest>({
profile_photo: null,
first_name: account.value?.first_name ?? "",
last_name: account.value?.last_name ?? "",
phone: account.value?.phone ?? "",
gender: account.value?.gender || undefined,
email: account.value?.email ?? "",
birth_date: account.value?.birth_date ?? "",
});
const profilePictureModalIsShow = ref(false);
const { isNotEqual, clear: clearObjectTracker } = useObjectTrack(personalData);
const alises = ref(["شکارچی", "آیفون باز", "خوش سلیقه", "دست و دلباز", "چرم باز"]);
// queries
const { mutateAsync: updateAccount, isPending: updateAccountIsPending } = useUpdateAccount();
// computed
const formRules = computed(() => {
return {
first_name: {
required: helpers.withMessage("فیلد نام الزامی می باشد", required),
minLength: helpers.withMessage("فیلد نام حداقل ۳ کرکتر می باشد", minLength(3)),
},
last_name: {
required: helpers.withMessage("فیلد نام خانوادگی الزامی می باشد", required),
minLength: helpers.withMessage("فیلد نام خانوادگی حداقل ۳ کرکتر می باشد", minLength(3)),
},
phone: {
required: helpers.withMessage("فیلد شماره تلفن الزامی می باشد", required),
phoneValidator: helpers.withMessage(
"شماره تلفن وارد شده معتبر نمی باشد",
helpers.regex(/^0?[1-9][0-9]{9}$/),
),
},
};
});
const formValidator$ = useVuelidate(formRules, personalData as any);
// methods
const updateData = () => {
updateAccount(
{ ...personalData.value },
{
onSuccess: (data) => {
queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.account],
});
addToast({
message: "اطلاعات با موفقیت تغییر یافت",
options: {
status: "success",
},
});
clearObjectTracker();
},
onError: () => {
addToast({
message: "خطایی در تغییر اطلاعات رخ داد",
options: {
status: "error",
},
});
},
},
);
};
const handleSubmit = (withValidation: boolean) => {
if (withValidation) {
formValidator$.value.$validate();
if (!formValidator$.value.$errors.length) {
updateData();
}
} else {
updateData();
profilePictureModalIsShow.value = false;
}
};
</script>
<template>
<div class="w-full flex flex-col gap-5">
<ProfilePageTitle
title="پروفایل"
icon="ci:bi-person-vcard"
@back="toggleSidebar"
/>
<div class="flex flex-col gap-6">
<div
class="w-full flex flex-col lg:flex-row items-center max-lg:gap-5 lg:justify-between border p-6 rounded-xl border-slate-200"
>
<div class="flex items-center justify-start gap-5 w-full lg:w-8/12">
<div class="relative shrink-0 rounded-full flex-center">
<Avatar
class="!size-20 lg:!size-32"
:src="account!.profile_photo"
:alt="
account?.first_name && account?.last_name
? `${account?.first_name.charAt(0)} ${account?.last_name.charAt(0)}`
: 'بدون نام کاربری'
"
/>
<ProfilePictureModal
v-model:is-show="profilePictureModalIsShow"
v-model="personalData.profile_photo!"
@update:model-value="() => handleSubmit(false)"
/>
</div>
<div class="flex flex-col gap-2 lg:gap-3">
<span class="typo-sub-h-md lg:typo-sub-h-lg"
>{{ account?.first_name }} {{ account?.last_name }}</span
>
<span class="typo-sub-h-xs lg:typo-sub-h-sm !font-light text-slate-600 leading-[200%]">
با اولین خریدتون هوش مصنوعی وبسایتمون واستون یک بایوگرافی درست میکنه :)
</span>
<div class="flex-center border border-yellow-500 pe-3.5 ps-1 w-max rounded-full">
<div class="rounded-full p-1.5 lg:p-2">
<Icon
name="ci:bi-patch-check"
class="**:fill-yellow-400"
size="20"
/>
</div>
<span class="text-[10px] lg:text-xs text-yellow-500">جزو ۳ مشتری برتر</span>
</div>
</div>
</div>
<div class="flex flex-col items-start gap-3 w-full lg:w-4/12">
<span class="typo-sub-h-md lg:typo-sub-h-lg">لقب های شما</span>
<span class="flex w-full flex-wrap gap-2">
<span
v-for="(alise, index) in alises"
:key="index"
class="flex-center bg-slate-50 border border-slate-200 py-1.5 lg:py-2 px-3 w-max rounded-full"
>
<span class="text-[10px] lg:text-xs text-black">{{ alise }}</span>
</span>
</span>
</div>
</div>
<ProfileSection title="اطلاعات شما">
<template #button>
<Button
v-if="isNotEqual"
:loading="updateAccountIsPending"
class="rounded-full w-[6.5rem]"
@click="handleSubmit(true)"
size="md"
>
<Icon
v-if="updateAccountIsPending"
name="ci:svg-spinners-3-dots-bounce"
/>
<span v-else> ثبت تغییرات </span>
</Button>
</template>
<div class="w-full grid grid-cols-1 lg:grid-cols-2 gap-x-3 gap-y-5">
<DataField
id="personal-data-name"
label="نام"
:error="formValidator$.first_name"
>
<Input
v-model="personalData.first_name!"
variant="outlined"
:error="formValidator$.first_name.$error"
/>
</DataField>
<DataField
id="personal-data-last-name"
label="نام خانوادگی"
:error="formValidator$.last_name"
>
<Input
v-model="personalData.last_name!"
variant="outlined"
:error="formValidator$.last_name.$error"
/>
</DataField>
<DataField
id="personal-data-gender"
label="جنسیت"
>
<Select
v-model="personalData.gender!"
:options="['مرد', 'زن']"
variant="outlined"
/>
</DataField>
<DataField
id="personal-data-birth-date"
label="تاریخ تولد"
>
<Datepicker v-model="personalData.birth_date!" />
</DataField>
<DataField
id="personal-data-phone"
label="تلفن همراه"
:error="formValidator$.phone"
>
<Input
v-model="personalData.phone!"
variant="outlined"
disabled
:error="formValidator$.phone.$error"
/>
</DataField>
<DataField
id="personal-email"
label="حساب الکترونیکی"
>
<Input
v-model="personalData.email!"
variant="outlined"
/>
</DataField>
</div>
<div class="w-full flex items-start justify-start gap-2 mt-5 px-2">
<Icon
name="ci:bi-info-circle-fill"
class="**:fill-slate-400 mt-0.5"
/>
<p class="text-slate-400 text-[13px] font-medium">
با پر کردن فیلد های جنسیت, حساب الکترونیکی, تاریخ تولد مارا در خدمات رسانی شخصی سازی شده به شما
مشتریان عزیز یاری کنید
</p>
</div>
</ProfileSection>
</div>
</div>
</template>
<style scoped></style>