Files
hossein-por-shop/frontend/components/profile/index/ProfilePictureModal.vue
T
2026-04-23 16:08:52 +03:30

186 lines
5.8 KiB
Vue

<script setup lang="ts">
// imports
import useUpdateAccount from "~/composables/api/account/useUpdateAccount";
import { useToast } from "~/composables/global/useToast";
// types
type Props = {
modelValue: File | null;
isShow: boolean;
};
type Emits = {
"update:modelValue": [value: File | null];
"update:isShow": [value: boolean];
};
// props
const props = defineProps<Props>();
const { modelValue, isShow } = toRefs(props);
// emits
const emit = defineEmits<Emits>();
// state
const visible = computed({
get: () => isShow.value ?? false,
set: (value: boolean) => emit("update:isShow", value),
});
const { addToast } = useToast();
const {
open: openFileDialog,
reset: resetFileDialog,
onChange: onFileDialogChange,
} = useFileDialog({
accept: ".jpg, .jpeg, .png",
directory: false,
});
const avatars = ref([
"/img/avatars/1.jpg",
"/img/avatars/2.jpg",
"/img/avatars/3.jpg",
"/img/avatars/4.jpg",
"/img/avatars/5.jpg",
]);
// queries
const { isPending: updateAccountIsPending } = useUpdateAccount();
// computed
const currentProfile = computed({
get: () => (!!modelValue.value ? URL.createObjectURL(modelValue.value) : null),
set: (value: File) => emit("update:modelValue", value),
});
// methods
onFileDialogChange((files: any) => {
const file = files[0];
if (file.size > 2 * 1024 * 1024) {
addToast({
message: "محدودیت حجم فایل حداکثر ۲ مگابایت می باشد",
options: {
status: "error",
},
});
return;
}
emit("update:modelValue", file);
resetFileDialog();
});
const resetAvatarFile = async () => {
const response = await fetch("/img/default-avatar.jpeg");
const blob = await response.blob();
const file = new File([blob], "default-avatar.jpeg", { type: blob.type });
emit("update:modelValue", file);
resetFileDialog();
};
</script>
<template>
<Modal
v-model="visible"
title="عکس پروفایل"
icon="bi:image"
iconSize="20"
contectClass="w-full max-lg:container lg:!w-[30vw]"
>
<template #trigger>
<button class="bg-black text-slate-100 rounded-full p-2 flex-center absolute -bottom-0 -right-0">
<Icon
name="bi:pencil"
class="**:fill-slate-100"
size="12"
/>
</button>
</template>
<template #content>
<div class="w-full flex flex-col-reverse items-center justify-between py-10 gap-10 px-4">
<div class="flex items-center justify-center w-full flex-wrap gap-4 max-w-[500px]">
<button
v-for="(avatar, index) in avatars"
:key="index"
class="size-16 lg:size-20 rounded-full focus:ring-2 focus:ring-offset-1 focus:ring-black transition-all"
>
<Avatar
:src="avatar"
:alt="`avatar-${index}`"
class="size-full"
/>
</button>
</div>
<div class="w-full flex-center gap-4 max-w-[500px] flex-wrap">
<button
class="size-6 lg:size-8 rounded-full bg-orange-100 whitespace-nowrap ring-2 hover:ring-black ring-slate-200 ring-offset-3"
/>
<button
class="size-6 lg:size-8 rounded-full bg-orange-200 whitespace-nowrap ring-2 hover:ring-black ring-slate-200 ring-offset-3"
/>
<button
class="size-6 lg:size-8 rounded-full bg-amber-600 whitespace-nowrap ring-2 hover:ring-black ring-slate-200 ring-offset-3"
/>
<button
class="size-6 lg:size-8 rounded-full bg-amber-700 whitespace-nowrap ring-2 hover:ring-black ring-slate-200 ring-offset-3"
/>
<button
class="size-6 lg:size-8 rounded-full bg-amber-800 whitespace-nowrap ring-2 hover:ring-black ring-slate-200 ring-offset-3"
/>
</div>
<div class="w-full flex-col-center gap-5">
<Avatar
:src="currentProfile"
alt=""
class="!size-24 lg:!size-32"
iconClass="!text-xl lg:!text-2xl"
/>
<div class="flex items-center gap-4">
<Button
class="rounded-full w-[8rem] bg-red-500"
@click="resetAvatarFile"
:loading="updateAccountIsPending"
size="md"
>
<Icon
v-if="updateAccountIsPending"
name="bi:trash"
/>
<span v-else>حذف عکس</span>
</Button>
<Button
class="rounded-full w-[8rem]"
@click="openFileDialog"
:loading="updateAccountIsPending"
size="md"
>
<Icon
v-if="updateAccountIsPending"
name="svg-spinners:3-dots-bounce"
/>
<span v-else>آپلود عکس شما</span>
</Button>
</div>
</div>
</div>
</template>
</Modal>
</template>
<style scoped></style>