feat: add withdraw request functionality and UI components

This commit is contained in:
2026-08-27 02:39:57 +03:30
parent aeef95339d
commit 4ce663c669
115 changed files with 4064 additions and 315 deletions
@@ -0,0 +1,132 @@
<script setup>
import { computed, ref, watch } from 'vue';
import { Inertia } from '@inertiajs/inertia';
import { usePage } from '@inertiajs/inertia-vue3';
import Dialog from 'primevue/dialog';
import UiButton from '../ui/UiButton.vue';
import UiInput from '../ui/UiInput.vue';
import { useGlobalStore } from '../../store/Global';
const visible = defineModel('visible');
const globalStore = useGlobalStore();
const { props } = usePage();
const form = ref({
fullname: '',
birthdate: '',
national_code: '',
email: '',
province: '',
city: '',
address: '',
});
const avatarFile = ref(null);
const avatarPreview = ref('');
const avatarInput = ref(null);
const errors = ref({});
const submitting = ref(false);
const avatarUrl = computed(() => avatarPreview.value || globalStore.current_user.profile_url || '');
watch(visible, (isOpen) => {
if (!isOpen) return;
const user = props.value.auth?.User || {};
form.value = {
fullname: user.fullname || '',
birthdate: user.birthdate || '',
national_code: user.national_code || '',
email: user.email || '',
province: user.province || '',
city: user.city || '',
address: user.address || '',
};
avatarFile.value = null;
avatarPreview.value = '';
errors.value = {};
});
const errorFor = (field) => {
const error = errors.value[field];
return Array.isArray(error) ? error[0] : (error || '');
};
const openAvatarPicker = () => {
avatarInput.value?.click();
};
const handleAvatarChange = (event) => {
const file = event.target.files?.[0];
if (!file) return;
avatarFile.value = file;
avatarPreview.value = URL.createObjectURL(file);
};
const submit = () => {
submitting.value = true;
Inertia.post('/dashboard/profile', {
...form.value,
avatar: avatarFile.value,
}, {
forceFormData: true,
preserveScroll: true,
onSuccess: () => {
globalStore.current_user = props.value.auth.User;
visible.value = false;
},
onError: (formErrors) => {
errors.value = formErrors;
},
onFinish: () => {
submitting.value = false;
},
});
};
</script>
<template>
<Dialog v-model:visible="visible" :modal="true" :closable="true" :dismissableMask="false" :maximizable="false" :style="{ width: '688px' , minHeight: '376px' }" class="fast-request-dialog" :pt="{header : '!bg-client-primaryBlue'}">
<template #header>
<div class="w-full flex items-center justify-start">
<h2 class="text-[14px] font-[700] text-[#576989]">ویرایش حساب کاربری</h2>
</div>
</template>
<form class="w-full flex flex-col gap-5 border-t border-[#EBEBEB] pt-5" @submit.prevent="submit">
<div class="flex items-center gap-4">
<div class="relative">
<span class="flex items-center justify-center w-20 h-20 rounded-full bg-[#F6F7F9] border border-[#DEE3EB] overflow-hidden">
<img v-if="avatarUrl" :src="avatarUrl" alt="تصویر پروفایل" class="w-full h-full object-cover" />
<i v-else class="fi fi-rr-user flex text-2xl text-[#576989]"></i>
</span>
<button type="button" class="absolute -bottom-1 -left-1 flex items-center justify-center w-7 h-7 rounded-full bg-dominant text-white border-2 border-white cursor-pointer" @click="openAvatarPicker">
<i class="fi fi-rr-camera flex text-[11px]"></i>
</button>
<input ref="avatarInput" type="file" accept="image/png,image/jpeg" class="hidden" @change="handleAvatarChange" />
</div>
<div class="flex flex-col gap-1">
<span class="text-[13px] font-[700] text-[#3E4C63]">تصویر پروفایل</span>
<span class="text-[11px] font-[400] text-[#576989]">فرمت‌های مجاز: png ،jpg — حداکثر ۱۰ مگابایت</span>
<span v-if="errorFor('avatar')" class="text-[11px] text-[#7A1419]">{{ errorFor('avatar') }}</span>
</div>
</div>
<div class="grid grid-cols-2 max-sm:grid-cols-1 gap-4">
<UiInput v-model="form.fullname" label="نام و نام خانوادگی" placeholder="نام و نام خانوادگی خود را وارد کنید" :error="errorFor('fullname')" />
<div class="flex flex-col gap-1">
<label class="ui-input-label">تاریخ تولد</label>
<CustomDatePicker v-model="form.birthdate" type="date" format="YYYY-MM-DD" display-format="jYYYY/jMM/jDD" class="w-full" />
<span v-if="errorFor('birthdate')" class="text-[11px] text-[#7A1419]">{{ errorFor('birthdate') }}</span>
</div>
<UiInput v-model="form.national_code" label="کد ملی" placeholder="کد ملی خود را وارد کنید" convertPersianNumbers :error="errorFor('national_code')" />
<UiInput v-model="form.email" label="ایمیل" type="email" placeholder="ایمیل خود را وارد کنید" direction="ltr" :error="errorFor('email')" />
<UiInput v-model="form.province" label="استان" placeholder="استان محل سکونت" :error="errorFor('province')" />
<UiInput v-model="form.city" label="شهر" placeholder="شهر محل سکونت" :error="errorFor('city')" />
</div>
<UiInput v-model="form.address" label="آدرس" placeholder="آدرس خود را وارد کنید" :error="errorFor('address')" />
<div class="w-full pt-3 flex items-center justify-start gap-2 border-t border-[#EBEBEB]">
<UiButton label="ذخیره تغییرات" severity="success" size="small" :loading="submitting" :disabled="submitting" @click="submit" />
<UiButton label="انصراف" severity="secondary" size="small" :disabled="submitting" @click="visible = false" />
</div>
</form>
</Dialog>
</template>